Get Start
Get Start
Get Start
DataDirect
®
for OLE DB
Getting Started
© 1999 MERANT. All rights reserved. Printed in the U.S.A.
No part of this publication, with the exception of the software product user
documentation contained on a CD-ROM, may be copied, photocopied,
reproduced, transmitted, transcribed, or reduced to any electronic medium or
machine-readable form without prior written consent of MERANT.
MERANT
701 East Middlefield Road
Mountain View, California 94043
3
Table of Contents
Preface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
What Is DataDirect for OLE DB? . . . . . . . . . . . . . . . . . . . . . . . . 7
Using This Guide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
Other Documentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
Conventions Used in This Guide . . . . . . . . . . . . . . . . . . . . . . . . 11
Typographical Conventions. . . . . . . . . . . . . . . . . . . . . . . . . 11
Mouse Conventions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
Keyboard Conventions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
Glossary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
Preface
Product The DataDirect for OLE DB products and their components are:
components
■ Connect OLE DB. This product includes a suite of data
providers for the most popular relational databases, like
Oracle, Informix, Sybase, and Lotus Notes. In addition, it
includes OLE DB data providers for nonrelational data stores,
like Microsoft Exchange, Microsoft Mail, and Lotus Mail.
Other Documentation
In addition to this book, the following documentation is
available:
Typographical Conventions
This guide uses the following typographical conventions:
Convention Explanation
italics Introduces new terms with which you may not be
familiar, and is used occasionally for emphasis.
bold Emphasizes important information. Also indicates
button, menu, and icon names on which you can act.
For example, click Next.
UPPERCASE Indicates the name of a file. For operating
environments that use case-sensitive filenames, the
correct capitalization is used in information specific
to those environments.
Also indicates keys or key combinations that you can
use. For example, press the ENTER key.
monospace Indicates syntax examples, values that you specify, or
results that you receive.
monospaced Indicates names that are placeholders for values that
italics you specify. For example, filename.
Convention Explanation
brackets [ ] Indicates optional items. For example, in the
following statement: SELECT [DISTINCT], DISTINCT is
an optional keyword.
Also indicates sections of the Windows Registry.
braces { } Indicates that you must select one item. For example,
{yes | no} means that you must specify either yes or
no.
ellipsis . . . Indicates that the immediately preceding item can
be repeated any number of times in succession. An
ellipsis following a closing bracket indicates that all
information in that unit can be repeated.
Mouse Conventions
This action… Means to…
Click Point to an object with the mouse pointer and
momentarily press the left mouse button.
Double-click Press the left mouse button twice.
Right-click Momentarily press the right mouse button.
Drag Press and hold the left mouse button while
dragging item(s) to another part of the screen.
SHIFT+Click Click an object to select it; then, press and hold
the SHIFT key. Click another object to select the
intervening series of objects.
CTRL+Click Press and hold the CTRL key; then, click a
selection. This lets you select or deselect any
combination of objects.
Keyboard Conventions
Select menu items by using the mouse or pressing ALT+ the key
letter of the menu name or item.
Internet
Telephone
Australia 1 800 335 664 or 8:30-5:30 p.m. Local Melbourne Time (LMT)
9816 9977 for
Melbourne Metro
Belgium 0800 724 61 9:00-6:30 p.m. CET
When you contact us, make sure that you can provide the
following information:
Business Data
Application Consumer
Interface
to Data Data
Consumer Provider
Interface
to Data
Store
Note that the data provider and the data store must also
communicate using a common interface. This interface is always
determined by the data store’s format.
Note: You can also use your ODBC-based applications with the
Connect OLE DB Data Providers by going through the Connect
ODBC/OLE DB Adapter. The Adapter gives you access to OLE DB
functionality through your current ODBC technology. For more
information, see “Using ODBC Applications with DataDirect
Connect OLE DB Components” on page 35.
User Application
(Data Consumer)
Sybase
Relational
Data Store
Oracle
Connect OLE DB Relational
OLE DB for Sybase Provider Data Store
Request
1 2 3 4
Connect OLE DB
for Exchange
Provider
Exchange
Connect OLE DB Nonrelational
for Notes Provider Data Store
Lotus Notes
Nonrelational
Data Store
User Application
(Data Consumer)
OLE DB
Request
SequeLink OLE DB
Provider
SequeLink Server Receives Request and
Manages Data Source Connections to
Multiple Databases on Multiple Platforms
Data Source
Connections
■ Improved performance
For a DBMS that does not have a native, relational data provider
available, using MSDASQL with an ODBC driver remains a good
solution. It allows a business to migrate to OLE DB technology,
and it can easily be used in the same environment with other
OLE DB components, including Connect OLE DB data providers,
SequeLink OLE DB Edition, and Reflector.
Figure 1-4. SQL Query Processing through DataDirect OLE DB Data Providers and
the Reflector Service Component
SequeLink
SequeLink OLE DB Server
Provider
SequeLink Multiple Relational
User Application Server is Data Stores on
(Data Consumer) Available for Multiple Platforms
Multiple (SQL Processors)
Platforms
Reflector Service
Component
Figure 1-5. How Businesses Can Use the Connect ODBC/OLE DB Adapter
ODBC DBMS
Driver (SQL Support)
OLE DB
Reflector SQL Relational Provider
Service Component
DataDirect OLE DB
Component Description of Features
Connect ODBC/OLE DB Transparently migrates ODBC technology to OLE DB by
Adapter connecting ODBC-based applications to OLE DB relational data
providers and service components.
Connect OLE DB for MAPI Connects a data consumer or other OLE DB component to the
Data Provider MAPI-based data stores of Microsoft Exchange, Microsoft Mail,
Lotus Mail, and Lotus cc:Mail.
Connect OLE DB for Notes Connects a data consumer or other OLE DB component to any
Data Provider Lotus Notes database.
Reflector Service Accepts OLE DB-based SQL queries from a data consumer and
Component processes them for execution by an OLE DB data provider. It
also formats and returns the results from the OLE DB data
provider to the data consumer. Reflector supports persistent
views, provides a metadata catalog, and supports scrollable
cursors across all types of data.
Connect OLE DB Connects a data consumer or other OLE DB component to a
Relational Data Providers DBMS. Each DBMS has its own Connect OLE DB Data Provider.
SequeLink OLE DB Edition Connects a data consumer on the thin SequeLink client to
SequeLink Server, which can connect to any database
supported by SequeLink Server. SequeLink Server is available
for Windows NT, UNIX, AS/400, and OS/390. SequeLink Server
supports the leading databases, including databases on legacy
systems. SequeLink uses only a single OLE DB Provider on the
client to access all supported relational databases on the server.
Table 2-1. Using DataDirect OLE DB Components: Users and Their Typical Tasks
Business
Application Application
Task Performed Users DBA Developer
Install ODBC/OLE DB Adapter and configure *
Adapter data sources
Install Connect OLE DB for MAPI and configure *
MAPI profiles
Install Connect OLE DB for Notes and configure *
Notes server locations
Install Connect OLE DB Relational Data Providers *
and configure provider data sources
Install Reflector and configure simple Reflector data *
sources
Configure complex Reflector data sources *
Install SequeLink OLE DB Edition Client and *
configure data sources for the SequeLInk OLE DB
Provider
Install SequeLink OLE DB Edition Server and *
configure SequeLink data sources that will be used
by the SequeLink OLE DB Provider
Write OLE DB applications that use DataDirect *
OLE DB components
■ Microsoft Exchange
■ Microsoft Mail
■ Lotus Mail
■ Lotus cc:Mail
Connect OLE DB If you select Connect OLE DB for MAPI as your provider, you can
for MAPI retrieve information about the email in your mail folders. This
information is returned to you as a table of rows and columns
(see Table 2-2 on page 43 for an example). You can read and
copy this tabular data into reports. Connect OLE DB for MAPI
does not support writing to this data, however, so you cannot
change it in the email data store.
For example, you could query for a row from the Inbox table
that identifies an incoming message, the sender of the message,
and the date it was sent, as shown in Table 2-2.
Connect OLE DB If you have installed Connect OLE DB for Notes, you will need to
for Notes tell it where your Lotus Notes databases are located. You
perform this configuration task with the Connect OLE DB for
Notes Administrator window (for details on using this window,
refer to Connect OLE DB User’s Guide and Reference). You can
also perform this task if you are trying to make a connection to a
Reflector data source that uses Connect OLE DB for Notes as the
underlying data provider. In this case, you can configure through
the Reflector Data Sources window (Figure 2-5 on page 51).
After you install Connect OLE DB for Notes, the data consumers
you use will list Connect OLE DB for Notes as a provider that you
can select for connecting to a Lotus Notes database. If you select
the Connect OLE DB for Notes data provider, a login window may
be displayed. After you log in, Connect OLE DB for Notes
connects you and you can query the Notes database for data.
Connect OLE DB for Notes returns the requested data.
For example, you can ask for a table showing the Notes databases
you have access to on a particular server or on the local client
system. Connect OLE DB for Notes considers each Notes database
to be a catalog.
Table 2-3 shows a sample of the data you can retrieve from a
Lotus Notes database using Connect OLE DB for Notes.
CATALOG_NAME DESCRIPTION
MAIL\JSMITH.NSF Jane Smith
ISupport.NSF Information Systems
Support Requests
To use any of these providers, you must first set up a data source
for the provider. Data sources for these providers are set up
using the INTERSOLV OLE DB Administrator. The Administrator
displays the names of the data sources you define and their
properties. It also lets you make changes to a data source.
For example, Figure 2-2 shows the Setup Assistant window for
Connect OLE DB for Oracle data sources. These setup windows
are similar to the data source setup windows for DataDirect
ODBC database drivers.
SQL queries on After configuration, your ODBC-based application will list the
data stores new ODBC data source as an available connection. If you select
it, your connection goes through the Connect ODBC/OLE DB
Adapter to the Reflector Data Sources window (Figure 2-5 on
page 51).
There are two ways you can define a Reflector data source:
Note: You can use another database and OLE DB data provider
for the Reflector catalogs you create. However, the database
must be a relational database, and the OLE DB data provider
must support reading and writing of SQL.
Make SQL queries Once you connect to a Reflector data source, you can use your
with Reflector business application to make SQL queries against the data store
data sources defined in that data source. For example, you could use Reflector
to query your Microsoft Exchange inbox for all email messages
you received from John Doe, sorted by the date you received
them. If you are connecting through a Reflector catalog, you can
create and store SQL views of that data.
■ Plan the user groups and connections for these data sources
■ Define a Reflector catalog and schemas for each data source
■ Assign group user IDs for login to data stores
■ Create SQL queries and store them as Reflector views
Defining Reflector You define Reflector data sources and create Reflector catalogs
data sources with through the Reflector Setup Assistant in the INTERSOLV OLE DB
catalogs Administrator. You can create a catalog for a data source while
you are defining the data source. You can also create catalogs
independently, and then later select one for a data source when
you define it.
The following list describes some of the OLE DB objects that can
be exposed by a provider, as explained in the Microsoft OLE DB
Software Developer’s Kit:
■ Command
■ Data Source
■ Enumerator
■ Error
■ Multiple Results
■ Rowset
■ Session
■ Transaction
■ TransactionOption
■ Data Source
■ Error
■ Rowset
■ Session
OLE DB objects Reflector supports the following objects for data consumers:
supported
■ Data Source Object
■ Sessions
■ Transactions (if supported by the underlying data provider)
■ Commands
■ Rowsets
■ Errors
■ Custom Error (standard OLE DB error object extension)
1 Initialize OLE.
http://www.microsoft.com/msj
Base-Level Interfaces
Base-level interfaces are the minimum set of objects and
interfaces both data and service providers must support. A
simple data provider that doesn’t support transactions or queries
through Command objects need only support only the required
interfaces, while more sophisticated data and service providers
will build on this foundation.
All data and service providers must support a Data Source Object
(DSO). A DSO represents a connection to a data source through
which you can operate on its data. The DSO is also the initial
object instantiated by calling CoCreateInstance for a given
OLE DB data or service provider’s CLSID or by binding to a
moniker. A DSO creates multiple sessions via DBSession objects
within a single connection context. Once instantiated, a data
consumer must always initialize a DSO with the
IDBInitialize::Initialize member function. Attempting to use a
DSO that lacks required scope or authentication information will
return an error. Scope includes information such as the name
and location of the data source and authentication information
if the data is not in an authenticated security environment. This
information is provided by a data consumer through the
IDBInitialize::Initialize member function.
First, you need to get some information about the column names
and column types. You’ll use this information to create bindings,
which is the next step. With the IRowsetInfo::GetProperties
member function, you can obtain information that describes the
capabilities of the Rowset through properties supported by a
Rowset: bookmarks, scrolling backwards, the maximum number
of open rows that can be active at the same time, and about 60
other properties. Individual Rowsets created by the same data or
service provider may have different properties depending on
how the Rowset was created and if it applies to the current
circumstances. For example, a data provider’s ability to support
the property might be affected by the current transaction or the
current query. This will become clearer when Command objects
are discussed.
Creating Bindings
Now that you know something about the Rowset, you must
create bindings. A binding associates a piece of memory in the
data consumer’s buffer with a column of data in a Rowset. It
consists of matching some persistent name or identifier of the
logical column to its physical ordering in the current Rowset. For
example, if a SQL statement like
Length indicates the true length of the data and is useful mainly
on variable-length data types. On fixed-length data types it is set
to the actual width on a read from a Rowset and ignored on a
write. On variable-length data types, length reports the true, full
length of the data on reads, and is taken as an override of the
true size on write.
The status part of the binding lets a data consumer get status
information when reading from or writing to a Rowset. Status
can indicate whether the value is null upon a read, if the value
suffered any change upon coercion, and any error state.
Creating Accessors
Now that you have established the bindings, you need to gather
them in an Accessor, which reads data from and optionally writes
data to a data provider. Accessors contain information or code to
pack and unpack rows held by the data provider. You use them
like handles on the access members of a row, helping the row to
“crack” its contents and deliver the columns you bound in the
previous step.
HACCESSOR hAccessor;
hr = pIAccessor->CreateAccessor(DBACCESSOR_READ |
DBACCESSOR_ROWDATA,
cBind, rgBind, 0,
NULL, &hAccessor );
HROW hRow [in] is the handle of the row from which you get the
data, and HACCESSOR hAccessor [in] is the handle of the
Accessor you want to use. void *pData [out] represents a
pointer to a buffer allocated by the data consumer in which to
return the data.
type in the binding. Then it places the coerced data in the data
consumer’s structure at the offset specified in the binding,
truncated if necessary to match the value of cbMaxLen in the
column binding.
You can call GetData any number of times. In each call, you can
pass the same values, a different Accessor, a different pointer to
a buffer, or different values for both. This means you can get as
many copies of the data as you want, and you can get them in
different types if alternate coercions are provided by a data
provider.
Releasing Rows
Finally, when you are finished with the Rowset, you must release
the Accessor. IAccessor::ReleaseAccessor releases the Accessor
and all the resources associated with it. The data consumer must
also release the Rowset with IRowset::Release after releasing the
Accessor.
Like all COM components, OLE DB data and service providers are
found in the registration database. Information about OLE DB
data and service providers is kept in the registration database
using the standard OLE Component Categories structure, which
allows OLE components to become members of arbitrary
categories. It also permits OLE components to describe
themselves in enough detail that they do not have to be
instantiated. Instantiation may be expensive or may require
connection to a remote machine, so it should be avoided if
possible.
ICommandText *pCommandText;
hr = pIDBCreateCommand::CreateCommand
(NULL, IID_ICommandText,
(IUnknown **) &pCommandText);
REFGUID rguidDialect
const LPWSTR pwszCommand
pICommandText->SetCommandText (DBGUID_SQL92,
"SELECT * FROM CUSTOMERS ORDER BY CITY");
Before you execute the command, you may want to register with
the Command object the properties you want supported by the
Rowsets returned by ICommand::Execute. By setting properties, a
data consumer can ask for the exact functionality it wants. This
affects how a Rowset is implemented by a data provider. It is also
how a data consumer can get the functionality it wants with
associated performance trade-offs. You can register with the
ICommandProperties::SetProperties function.
HRESULT hr;
ULONG ulRowsets = 0;
This gives you a Rowset interface and lets you manipulate the
data using the Rowset cotypes.
Transactions
OLE DB has defined a set of interfaces and an object to support
simple, nested, and coordinated transactions. Any data that is
read, added, changed, deleted, or updated can be transacted.
Transaction support is provided through the ITransactionLocal
interface supported by the DBSession object. Any data provider
that supports transactions must support this interface.
ErrorObjects
The last object and set of interfaces to introduce before moving
to the sample application is the ErrorObject and its interfaces.
ErrorObjects are optional; they don’t have to be implemented by
a data service provider.
http://www.microsoft.com/data
REGSVR32 CHECKDP.DLL
CHECKBOOK.EXE
http://www.microsoft.com/msj
ADO looks very similar to Data Access Objects (DAO) and Remote
Data Objects (RDO) because Microsoft took the best features of
both models and put them together in a common programming
model. The most important points to make here are that ADO is
easy to use, language-independent, and provides extensible
interfaces for programmatic access to all types of data.
All objects in ADO (see Figure A-5 on page 91) can be instantiated
on their own, except the Error and Field objects. The hierarchy of
objects found in previous models like DAO and RDO is de-
emphasized in the ADO model to allow greater flexibility in
reusing objects in different contexts. For example, you can create
a Command object, associate and execute it against one
connection, then associate it with a different connection and re-
execute it. This approach also paves the way for you to create
specialized objects (defined at design time) and temporary,
unattached recordsets.
probably the object that you start with when working with any
database management system. It allows for things like
customization of connection options (such as your isolation level)
and starting and ending transactions. The Connection object also
allows you to execute commands such as a query or SQL
statement. To execute any kind of command, you use the Execute
method of the Connection object. If the command returns rows, a
default Recordset object is created and returned.
Those of you who remember DAO and RDO will remember there
used to be a separate execute from OpenRecordSet, and you
always had to know if you were going to get results or not to
use one or the other. In the ADO model, Microsoft cleaned that
up. All you really need is one Execute method—you don't need
to know which one or whether you are going to get results or
not. If there are results returned from the query, the Execute
method will return a Recordset object. If there are no results
returned from the query, it will return nothing. Microsoft has
eliminated a lot of the bulk from the model.
rs.MoveNext
WEnd
The above code sample shows the Open statement taking some
optional arguments. These arguments are optional because there
are properties on the Recordset that are equivalent to them.
What Microsoft did is give you some shorthand. Using the
shorthand is a definite performance gain, especially when used
with scripting languages.This example is passing in a generic SQL
statement, a source that is represented as a Connection object,
and constantly informing the Recordset object to use a Keyset
cursor when it is opened.
cmd.ActiveConnection = "<con.string>"
cmd.CommandText = "<sql>"
cmd.Parameters(0) = <value>
cmd.Execute
cmd.Parameters(0) = <value>
cmd.Execute
http://www.microsoft.com/data
ADO is also distributed with ASP for use with the Microsoft
Internet Information Server (IIS) and the OLE DB Software
Development Kit.
Conclusion
The goal of OLE DB is to give applications uniform access to data
stored in DBMS and non-DBMS applications. Using OLE DB,
applications can take advantage of the benefits of database
technology without having to transfer data from its place of
origin to a DBMS.
Figure A-6. Checkbook: Sample Data Consumer © 1997 Microsoft Corporation. All
rights reserved.
///////////////////////////////////////////////////////////////////
// CHECKBOOKDOC.CPP
//
////////////////////////////////////////////////////////////////
#include "stdafx.h"
#define DBINITCONSTANTS
#include "oledb.h"
#include "viewhints.h"
#include "row.h"
#include "Checkbook.h"
#include "CheckbookDoc.h"
#include "checkview.h"
#include "ledgerview.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////
// CCheckbookDoc
IMPLEMENT_DYNCREATE(CCheckbookDoc,CDocument)
BEGIN_MESSAGE_MAP(CCheckbookDoc,CDocument)
//{{AFX_MSG_MAP(CCheckbookDoc)
ON_COMMAND(ID_EDIT_NEW_CHECK, OnNewCheck)
ON_COMMAND(ID_NEXT_CHECK, OnNextCheck)
ON_UPDATE_COMMAND_UI(ID_NEXT_CHECK, OnUpdateNextCheck)
ON_COMMAND(ID_PREV_CHECK, OnPrevCheck)
ON_UPDATE_COMMAND_UI(ID_PREV_CHECK, OnUpdatePrevCheck)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
BEGIN_DISPATCH_MAP(CCheckbookDoc, CDocument)
//{{AFX_DISPATCH_MAP(CCheckbookDoc)
// NOTE - the ClassWizard will add and remove mapping macros here.
// DO NOT EDIT what you see in these blocks of generated code!
//}}AFX_DISPATCH_MAP
END_DISPATCH_MAP()
/////////////////////////////////////////////////////////////////
// Cleaup the memory that was used to hold the check information in the CObList
m_oblRows.
while ((pos = m_oblRows.GetHeadPosition())
!= NULL)
{
pRow = (CRow *)m_oblRows.GetAt(pos);
m_oblRows.RemoveAt(pos);
if (pRow != NULL)
delete pRow;
}
// Release the rowset interface m_pIRowset->Release();
m_pIRowset = NULL;
}
// Un-Initialize the DSO if (m_pIDBInitialize != NULL)
{
m_pIDBInitialize -> Uninitialize();
m_pIDBInitialize->Release();
m_pIDBInitialize = NULL;
}
}
/////////////////////////////////////////////////////////////////
// CCheckbookDoc diagnostics
#ifdef _DEBUG
void CCheckbookDoc::AssertValid() const
{
CDocument::AssertValid();
}
void CCheckbookDoc::Dump(CDumpContext&dc) const
{
CDocument::Dump(dc);
}
#endif
//_DEBUG
/////////////////////////////////////////////////////////////////
// CCheckbookDoc commands
BOOL CCheckbookDoc::OnOpenDocument(LPCTSTR lpszPathName)
{
HRESULT hr = S_OK;
if (!CDocument::OnOpenDocument(lpszPathName))
return FALSE;
// We have the file name, lets get the CLSID for the Check
// Book Data Provider so that an instance can be created.
CLSID CLSID_OLEDBObject;
{
// There was a failure, cleanup all interfaces and do not create the document
CleanUp();
return FALSE;
}
return TRUE;
}
// initializes the Check Book data provider by providing the file
// name of the check book .chb file.
HRESULT CCheckbookDoc::InitializeDSO (LPCTSTR lpszPathName)
{
HRESULT hr = S_OK;
GUID rgOptionIDs[1];
VARIANT rgOptionVals[1];
VariantInit(&rgOptionVals[0]);
rgOptionIDs[0] = DBINIT_OPT_NAME;
rgOptionVals[0].vt = VT_BSTR;
CString strPathName = lpszPathName;
rgOptionVals[0].bstrVal = strPathName.AllocSysString();
{
hr = pIOpenRowset -> OpenRowset(NULL, NULL, 0, NULL, IID_IRowset, NULL,
(IUnknown **)&m_pIRowset);
pIOpenRowset -> Release();
}
}
return hr;
}
// Retrieves an IAccessor interface and create an accessor that is used for reading
// and writing checks.
HRESULT CCheckbookDoc::CreateAccessor ()
{
HRESULT hr = S_OK;
hr = m_pIRowset -> QueryInterface(IID_IAccessor, (void **)&m_pIAccessor);
if (hr =--= S_OK)
{
ULONG ulErrorBinding = (ULONG)-1L;
hr = m_pIAccessor->CreateAccessor(DBACCESSOR_READWRITE | DBACCESSOR_ROWDATA,
m_ulBindings, m_prgBindings, 0,
&ulErrorBinding, &m_hAccessor);
}
return hr;
}
// Releases all of the HROWS from within the data provider.
HRESULT CCheckbookDoc::ReleaseRows()
{
ASSERT (m_pIRowset);
ULONG ulRows;
HRESULT hr = S_OK;
HROW *prghRows = NULL;
ulRows = m_oblRows.GetCount();
prghRows = (HROW *) CoTaskMemAlloc((sizeof (HROW *)) * ulRows);
if (prghRows != NULL)
{
HROW *pTempRow;
pTempRow = prghRows;
POSITION pos = NULL;
CRow *pRow = NULL;
for (ULONG ulIndex = 0; ulIndex < ulRows; ulIndex++)
{
pos = m_oblRows.FindIndex (ulIndex);
pRow = (CRow *)m_oblRows.GetAt(pos);
*pTempRow++ = pRow -> m_hRow;
}
CoTaskMemFree((void *) pRowData);
}
}
pIRowsetChange -> Release();
return hr;
}
// Function creates a new record by using the ulIndexsetNewRow interface supported by the
// data provider. The new row is added to the CObList and the views are updated
// accordingly.
HRESULT CCheckbookDoc::AddNewCheck()
{
BYTE *pRowData = NULL;
HROW *prghRows = NULL;
HRESULT hr = S_OK;
ASSERT (m_pIRowset);
ASSERT (m_hAccessor);
ASSERT (m_pColumnInfo);
ASSERT (m_ulMaxRowSize != 0);
pRowData = (BYTE *) CoTaskMemAlloc (m_ulMaxRowSize);
if (!pRowData)
return E_OUTOFMEMORY;
IRowsetNewRow *pIRowsetNewRow = NULL;
hr = m_pIRowset->QueryInterface(IID_IRowsetNewRow, (void **)&pIRowsetNewRow);
if (hr =--= S_OK)
{
HROW phRow = NULL;
if ((hr = pIRowsetNewRow -> SetNewData (NULL, m_hAccessor, pRowData,
&phRow)) =--= S_OK)
{
StoreRowData (phRow, pRowData);
// Make this the active check and update the ledger view.
m_nActiveRecord = m_oblRows.GetCount() - 1;
UpdateAllViews(NULL, VIEWHINT_ADD_CHECK);
}
else
CoTaskMemFree (pRowData);
pIRowsetNewRow -> Release();
}
return S_OK;
}
BOOL CCheckbookDoc::MaybeCommitDirtyCheck()
{
CView *pView;
POSITION pos = GetFirstViewPosition();
// Commands
void CCheckbookDoc::OnNewCheck()
{
// Before creating a new record,which will become the new selection, ask the user
// whether he or she wants to commit data entered in the check view for the
// previously selected check.
if (!MaybeCommitDirtyCheck())
return;
AddNewCheck();
}
void CCheckbookDoc::OnNextCheck()
{
ChangeSelectionNextCheckNo(TRUE);
}
void CCheckbookDoc::OnUpdateNextCheck(CCmdUI* pCmdUI)
{
pCmdUI->Enable(m_nActiveRecord < (GetNumberOfRows() - 1));
}
void CCheckbookDoc::OnPrevCheck()
{
ChangeSelectionNextCheckNo(FALSE);
}
void CCheckbookDoc::OnUpdatePrevCheck(CCmdUI* pCmdUI)
{
pCmdUI->Enable(m_nActiveRecord > 0);
}
// Function retieves column information from the data provider.
HRESULT CCheckbookDoc::GetColumnInfo ()
{
ASSERT (m_pIRowset);
ULONG pcColumns;
HRESULT hr = S_OK;
DBCOLUMNINFO *prgInfo = NULL;
WCHAR *ppStringsBuffer = NULL;
// Get an interface pointer to IColumnsInfo
IColumnsInfo *pIColumnsInfo = NULL;
hr = m_pIRowset -> QueryInterface(IID_IColumnsInfo,(void **)&pIColumnsInfo);
if (hr =--= S_OK)
{
// Get the Column Information
hr = pIColumnsInfo -> GetColumnInfo(&pcColumns,(DBCOLUMNINFO **)&prgInfo,
(WCHAR **)&ppStringsBuffer);
pIColumnsInfo -> Release();
if (hr =--= S_OK)
{
m_cColumns = pcColumns;
m_pColumnInfo = prgInfo;
CoTaskMemFree (ppStringsBuffer);
ppStringsBuffer = NULL;
}
}
return hr;
}
// Function creates bindings that map the data in the rowset’s columns
// to the check book data consumers buffer.
HRESULT CCheckbookDoc::SetupBindings ()
{
ASSERT (m_pColumnInfo);
UINT cBinding = 0;
DWORD dwOffset = 0;
// Since the check book data consumer displays all information about a check, and
// that’s all that the data provider provides, get all of the data.
for (ULONG ulIndex=0; ulIndex < m_cColumns; ulIndex++)
{
m_prgBindings[cBinding].dwPart = DBCOLUMNPART_VALUE |
DBCOLUMNPART_LENGTH | DBCOLUMNPART_STATUS;
m_prgBindings[cBinding].eParamIO= DBPARAMIO_NOTPARAM;
m_prgBindings[cBinding].iColumn= m_pColumnInfo[ulIndex].iNumber;
m_prgBindings[cBinding].dwType= m_pColumnInfo[ulIndex].dwType;
m_prgBindings[cBinding].pTypeInfo= m_pColumnInfo[ulIndex].pTypeInfo;
m_prgBindings[cBinding].pNum= NULL;
m_prgBindings[cBinding].obValue= dwOffset + offsetof(COLUMNDATA,bData);
m_prgBindings[cBinding].obLength= dwOffset + offsetof(COLUMNDATA, dwLength);
m_prgBindings[cBinding].obStatus= dwOffset + offsetof(COLUMNDATA, dwStatus);
m_prgBindings[cBinding].cbMaxLen=
m_pColumnInfo[ulIndex].dwType=--=
DBTYPE_STR ? m_pColumnInfo[ulIndex].cbMaxLength +
sizeof(char) : m_pColumnInfo[ulIndex].cbMaxLength;
m_prgBindings[cBinding].pObject.pUnkOuter = NULL;
m_prgBindings[cBinding].pObject.iid = IID_NULL;
m_prgBindings[cBinding].pObject.pbc = NULL;
dwOffset += m_prgBindings[cBinding].cbMaxLen + offsetof
(COLUMNDATA, bData); cBinding++;
}
m_ulBindings = cBinding;
m_ulMaxRowSize = dwOffset;
return S_OK;
}
HRESULT CCheckbookDoc::GetData ()
{
HRESULT hr = S_OK;
HROW *prghRows = NULL;
BYTE *pRowData = NULL;
ULONG ulRowsObtained;
ASSERT (m_pIRowset);
ASSERT (m_hAccessor);
ASSERT (m_pColumnInfo);
while (TRUE)
{
hr = m_pIRowset->GetNextRows(0, 0, 20, &ulRowsObtained, &prghRows);
if (FAILED(hr))
return hr;
// check to see the data provider returned any rows
if (ulRowsObtained =--= 0)
break;
// For each row, get the checkbook data from the ata provider
for (ULONG ulIndex = 0; ulIndex < ulRowsObtained; ulIndex++ )
{
pRowData = (BYTE *) CoTaskMemAlloc(m_ulMaxRowSize);
if (!pRowData)
return E_OUTOFMEMORY;
hr = m_pIRowset->GetData(prghRows[ulIndex], m_hAccessor, pRowData );
if (FAILED(hr))
{
// Free the task memory allocated by the data provider
CoTaskMemFree(prghRows);
if (pRowData)
CoTaskMemFree(pRowData);
return hr;
}
StoreRowData (prghRows[ulIndex], pRowData);
m_nActiveRecord = m_oblRows.GetCount() - 1;
}
// Free the task memory allocated by the data provider
CoTaskMemFree(prghRows);
prghRows = NULL;
}
return S_OK;
}
HRESULT CCheckbookDoc::StoreRowData
(HROW phRow, BYTE *pData)
{
{
switch (m_prgBindings[ulIndex].dwType)
{
case DBTYPE_UI8:
// Make sure this is the Amount Column
if (!m_prgBindings[ulIndex].iColumn =--=
CB_AMOUNT_ORDINAL)
hr = E_UNEXPECTED;
break;
case DBTYPE_UI4:
// Make sure this is the Check Number Column
if (!m_prgBindings[ulIndex].iColumn =--=
CB_CHECKNO_ORDINAL)
hr = E_UNEXPECTED;
break;
case DBTYPE_STR:
// Process the string related columns
if (!(m_prgBindings[ulIndex].iColumn =--=
CB_PAYTO_ORDINAL ||
m_prgBindings[ulIndex].iColumn =--= CB_DATE_ORDINAL ||
m_prgBindings[ulIndex].iColumn =--= CB_MEMO_ORDINAL))
hr = E_UNEXPECTED;
break;
default:
hr = E_UNEXPECTED;
break;
}
if (hr != S_OK)
break;
}
}
if (hr != S_OK)
delete pRow;
else
{
// Save the check information i a CObList
pRow -> m_hRow = phRow;
pRow -> m_pData = pData;
m_oblRows.AddTail (pRow);
}
return hr;
}
HRESULT CCheckbookDoc::CopyCheckDataIntoBuffer BYTE *pData, UINT nCheckNo,
DWORD dwCents, LPCTSTR lpszPayTo, LPCTSTR lpszDate, LPCTSTR
lpszMemo)
{
HRESULT hr = S_OK;
COLUMNDATA* pColumn;
DWORD dwStatus, dwLength;
ASSERT (offsetof(COLUMNDATA, dwLength) =--= 0);
for (ULONG ulIndex=0; ulIndex < m_ulBindings; ulIndex++)
{
pColumn = (COLUMNDATA *) (pData +
m_prgBindings[ulIndex].obLength);
dwStatus = pColumn->dwStatus;
dwLength = pColumn->dwLength;
switch (m_prgBindings[ulIndex].dwType)
{
case DBTYPE_UI8:
if (m_prgBindings[ulIndex].iColumn =--=
CB_AMOUNT_ORDINAL)
*(unsigned long *)pColumn->bData = dwCents;
else
hr = E_UNEXPECTED;
break;
case DBTYPE_UI4:
if (m_prgBindings[ulIndex].iColumn =--=
CB_CHECKNO_ORDINAL)
*(unsigned int *)pColumn->bData = nCheckNo;
else
hr = E_UNEXPECTED;
break;
case DBTYPE_STR:
if (m_prgBindings[ulIndex].iColumn =--=
CB_PAYTO_ORDINAL)
_tcscpy((TCHAR *) &pColumn->bData,lpszPayTo);
else if (m_prgBindings[ulIndex].iColumn =--=
CB_DATE_ORDINAL)
_tcscpy((TCHAR *) &pColumn->bData, lpszDate);
else if(m_prgBindings[ulIndex].iColumn =--=
CB_MEMO_ORDINAL)
_tcscpy((TCHAR *) &pColumn->bData, lpszMemo);
else
hr = E_UNEXPECTED;
break;
default:
hr = E_UNEXPECTED;
break;
}
if (hr != S_OK)
break;
}
return hr;
}
This appendix introduces the model of data access over the Web
using OLE DB data providers. You can find specific information
about implementing a Web-based data access solution from the
Microsoft World Wide Web site.
Figure B-1. Overview of Data Access Over the Web Using OLE DB Data Providers
DBMS
2 4
1 3 5
Figure B-2. Components Required to Use OLE DB Providers over the Web
Microsoft Windows NT
DBMS
Server 4.0 or higher
Data Stores
ODBC OLE DB
1 Call SQLAllocHandle to allocate a 1 Build an array of property structures
connection handle. describing the authentication
information, such as user ID,
2 Build a connection string containing
password, and the name of the data
keywords for authentication
source, as well as the level of
information, such as user ID,
prompting and the application’s
password, and the name of the data
window handle when appropriate.
source.
2 Call IDBProperties::SetProperties to
3 Call SQLDriverConnect providing the
set initialization properties.
connection string and other
information, such as level of 3 Call IDBInitialize::Initialize to
prompting and the application’s initialize the data source object.
window handle where appropriate.
Executing a Command
Table C-2 summarizes the steps to execute a command:
4 Calls ICommandPrepare::Prepare if it
wants to prepare the query for repeated
execution.
The data 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 data 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.
■ Can be called for any held row handle, not just the current
row
■ Calls SQLDescribeCol or
SQLColAttribute to describe each
column in the result set.
Glossary
Binary Large Object Any binary or character data larger than a certain
(BLOB) number of bytes (for example, the MAPI documentation
defines a range of 4 KB to 16 KB). In OLE DB, BLOBs are
generally sent and retrieved from the data source in
parts.
data store The data that the user wants to access, such as the data in
a database, file, or array. A data store owns data and
exposes its data in a tabular form as a rowset over a
native data format. Data stores can include a full SQL
DBMS, an ISAM file, or a text file or data stream.
data type The type of a piece of data. The data type of a consumer
variable, a parameter, or a column is identified by a type
indicator.
fetch To retrieve one or more rows from the data store and
instantiate them in a rowset.
file moniker A persistent file name that contains information that can
be used to identify objects that are saved as files. It is
based on a path in the file system.
foreign key One or more columns that match the primary key in
another table.
nonrelational data Data that is not stored with logical relationships attached
to it. For example, email data and word processing files
are nonrelational data. Contrast with relational data.
property group The set of all properties that apply to a particular OLE DB
object. For example, the Rowset property group specifies
whether a rowset supports bookmarks and whether it
can fetch backwards.
query processor Given a statement in some query language (that is, SQL),
a query processor determines a query plan for a
statement, executes the query plan, and returns a
result set.
service provider (OLE DB 1.0 and OLE DB 1.5) An OLE DB component that
exposes OLE DB interfaces for complex data processing
services. See service component.
Universal Data Access A Microsoft strategy based on ADO, OLE DB, and ODBC
(UDA) that lets users get to data in different data stores through
a common set of interfaces, regardless of where the data
resides or what type of data it is.
Index
A D
accessor data access through OLE DB 19
creating 74 data source
using in OLE DB 130 defining with Reflector 49
ActiveX Data Objects (ADO) 89 locating 131
ADO, sample data consumer call 62 data source object, initializing 124
DataDirect for OLE DB,overview 7
DataDirect OLE DB components
advantages 32
B examples of use 32
summary of products and features 37
bindings, creating 71 descriptor handles 130
books, for DataDirect OLE DB products 9 documentation, related 9
C E
Command object 97 Error object 94
Connect OLE DB ErrorObject 85
basics 21 executing a command 126
overview of Connect OLE DB
Nonrelational Data Providers 42
Connect OLE DB for MAPI 42
Connect OLE DB for MAPI typical tasks 41 F
Connect OLE DB for Notes 42
Connect OLE DB for Notes typical tasks 41 Field object 94
Connect OLE DB Relational Providers,
using 44
Connection object 91, 95
contacting Technical Support 13 G
Conventions used in this manual 11
creating bindings 71 getting a session 126
L R
leveraging OLE DB and ADO 98
Recordset object 93, 95
Reflector
data source definitions 49
M database administrator uses 53
introducing 49
overview 29
Manual conventions 11 sample ADO data consumer call 62
typical tasks 41
retrieving a row of data 128
rowset
O methods of reading 77
minimum requirement 68
object releasing rows 77
ADO 89
Command 97
command 92
Connection 91, 95 S
Error 94
Field 94 schema 142
Parameter 93 SequeLink OLE DB Edition
Recordset 93, 95 basic concepts 24
objects and interfaces description 8
base-level interfaces 67 SequeLink OLE DB Provider
introducing OLE DB 66 and OLE DB technology 24
OLE DB using 44
advantages 26 service component, overview 28
and Connect OLE DB Data Providers 21
and SequeLink OLE DB Edition 24
comparing to ODBC 123
SQL
making requests to nonrelational data
stores 27
making requests to relational data
stores 30
SupportNet 13
T
Technical Support, contacting 13
transactions, overview 84
W
Web data access and OLE DB data
providers 117
WWW, using to contact technical support 13