Accessors
are managed through the rowset interface IAccessor. The client application
uses the IAccessor interface to specify a set of column bindings and
to associate them with an accessor handle. The handle can be passed
into other rowset methods that manipulate individual rows of data.
When you specify column bindings and create accessors, consider these
questions:
-
How will the column be identified?
-
What type can the client bind it
to?
-
How much space will be needed to
store the data in the client's buffers?
If you
have a DBCOLUMNINFO structure for a column, you can easily transfer
that information into a DBBINDING structure, as shown in the following
table:
DBCOLUMNINFO Members Mapped to DBBINDING Members
The value
of iOrdinal must be the same in both the DBBINDING and DBCOLUMNINFO
structures. However, the other members in a DBBINDING structure do
not have to match the corresponding members that are returned in a
DBCOLUMINFO structure. For example, the wType value in the DBBINDING
structure can have any meaningful DBTYPE because the providers support
converting data between types as defined by the OLE DB specification.
In addition, the values of the DBBINDING members cbMaxLen, bPrecision,
and bScale are more closely related to the wType member of the DBBINDING
structure than are the corresponding members of the DBCOLUMNINFO structure.
If the
column metadata is known at the time that you are creating the client
application, then you do not need to query the data provider. Instead,
you can build static DBBINDING structures, which can increase run-time
performance. To build static DBINDING structures, you must know how
to map data set variable attributes to DBBINDING structure members.
The following table explains the mapping:
DBBINGING Structure Members and Corresponding Variable Attributes
DBBINDING Structure
Member
|
Corresponding
SAS Variable Attribute
|
|
Set this member
to the variable number that you want to bind to. The member should
be a value from 1 to N, where N is the number of variables that are
defined in the data set. (The value 0 is reserved for the self-bookmark
column.)
|
|
These three members
determine the offset of the column value, its length, and its status
of coercion in the buffer that you pass to the provider when you read
and update. If the corresponding bit is not set in the dwPart member,
these members are ignored.
|
|
Ignored. Reserved
for future use.
|
|
Unsupported.
OLE objects cannot be embedded in SAS data sets.
|
|
Used for formatting
data.
|
|
This bitmask
defines which obValue, obLength, and obStatus members are meaningful.
See the OLE DB specification for more information about this field.
|
|
This member does
not map to any SAS variable attributes. You use as described by the
OLE DB specification.
|
|
|
|
The number of
bytes allocated in your buffer for the data value. This number is
determined by the value of wType.
|
|
|
|
A DBTYPE value
that indicates the type that is used to represent the data value.
This value can directly correspond to the SAS variable's type (DBTYPE_WSTR
for character variables; DBTYPE_R8 for numeric variables), or it
can be any type that the provider can convert the data to. The OLE
DB specification lists the valid conversions between DBTYPEs, and
the SAS providers support all of these conversions. At run time you
can discover which conversions are allowed by calling the IConvertType::CanConvert()
rowset method.
|
|
The IOM provider
looks only at this member when wType is set to DBTYPE_NUMERIC. In
this case, this field defines the maximum precision to use when you
get the data. It is ignored when you set and update data.
|
|
|
In the
following example, you open a rowset component named pRowset on a
known data set named AUTOS, which contains these variables:
-
MAKE, which is a 40-byte character
variable
-
MODEL, a numeric variable
-
-
Bind MAKE
as DBTYPE_BSTR and the remaining variables as DBTYPE_STR. This binding
means that the first column is a fixed length and the other three
columns are varying lengths. So, cbMaxLen and obLength are not considered
when you bind MAKE, but they are considered when you bind to MODEL,
MPG, and COST.
Put the
values of MODEL, MPG, and COST into 15-byte buffers. Buffer your consumer
output to place the status and value of MAKE first, followed by the
lengths, status, and values of MODEL, MPG and COST. The following
code fills in an array of DBBINDING structures to match the described
binding and calls the appropriate method to create the OLE DB accessor
handle:
IAccessor * pAccessor;
DBBINDING rgBindings[4];
DBBINDSTATUS rgStatus[4];
HACCESSOR hAccessor;
ULONG ulOffset;
// Lay out each column in memory.
struct COLUMNDATA
{
DWORD dwLength; // length of data returned
DWORD dwStatus; // status of column
BYTE bData[1]; // data here and beyond
};
// Rounding amount is always a power of two.
#define ROUND_UP( Size, Amount ) (((DWORD)(Size) + ((Amount) - 1)) & ~((Amount) - 1))
// Alignment for placement of each column within memory.
// Rule of thumb is "natural" boundary, such as a 4-byte member should be
// aligned on address that is multiple of 4.
// Worst case is double or __int64 (8 bytes).
#define COLUMN_ALIGNVAL 8
memset( rgBindings, 0, sizeof( rgBindings ) ); // defensive programming
// Start laying out data at the beginning of our buffer
ulOffset = 0;
rgBindings[0].iOrdinal = 1; rgBindings[0].dwPart = DBPART_VALUE | DBPART_STATUS;
rgBindings[0].obValue = ulOffset + offsetof( COLUMNDATA, bData );
rgBindings[0].obStatus = ulOffset + offsetof( COLUMNDATA, dwStatus );
rgBindings[0].wType = DBTYPE_BSTR;
// Account for space taken by actual cell value
ulOffset += sizeof( BSTR ) + offsetof( COLUMNDATA, bData );
ulOffset = ROUND_UP( ulOffset, COLUMN_ALIGNVAL ); // round up for alignment
rgBindings[1].iOrdinal = 2; rgBindings[1].cbMaxLen = 15;rgBindings[1].dwPart = DBPART_VALUE |
DBPART_STATUS | DBPART_LENGTH; rgBindings[1].obValue = ulOffset +
offsetof( COLUMNDATA, bData );rgBindings[1].obLength = ulOffset +
offsetof( COLUMNDATA, dwLength );rgBindings[1].obStatus = ulOffset +
offsetof( COLUMNDATA, dwStatus );rgBindings[1].wType = DBTYPE_STR;
// Account for space taken by actual cell value
ulOffset += rgBindings[1].cbMaxLen + offsetof( COLUMNDATA, bData );
ulOffset = ROUND_UP( ulOffset, COLUMN_ALIGNVAL ); // round up for alignment
rgBindings[2].iOrdinal = 3; rgBindings[2].cbMaxLen = 15;rgBindings[2].dwPart = DBPART_VALUE |
DBPART_STATUS | DBPART_LENGTH; rgBindings[2].obValue = ulOffset +
offsetof( COLUMNDATA, bData );rgBindings[2].obLength = ulOffset +
offsetof( COLUMNDATA, dwLength );rgBindings[2].obStatus = ulOffset +
offsetof( COLUMNDATA, dwStatus );rgBindings[2].wType = DBTYPE_STR;
// Account for space taken by actual cell value
ulOffset += rgBindings[2].cbMaxLen + offsetof( COLUMNDATA, bData );
ulOffset = ROUND_UP( ulOffset, COLUMN_ALIGNVAL ); // round up for alignment
rgBindings[3].iOrdinal = 4; rgBindings[3].cbMaxLen = 15;rgBindings[3].dwPart = DBPART_VALUE |
DBPART_STATUS | DBPART_LENGTH; rgBindings[3].obValue = ulOffset +
offsetof( COLUMNDATA, bData );rgBindings[3].obLength = ulOffset +
offsetof( COLUMNDATA, dwLength );rgBindings[3].obStatus = ulOffset +
offsetof( COLUMNDATA, dwStatus );rgBindings[3].wType = DBTYPE_STR;
// Account for space taken by actual cell value
ulOffset += rgBindings[3].cbMaxLen + offsetof( COLUMNDATA, bData );
ulOffset = ROUND_UP( ulOffset, COLUMN_ALIGNVAL ); // round up for alignment
pRowset->QueryInterface( IID_IAccessor, &hAccessor );
pAccessor->CreateAccessor( DBACCESSOR_ROWDATA, // we are binding to columns in a rowset
4, // binding 4 columns
rgBindings,
0, // ignored for rowset bindings
&hAccessor,
rgStatus );
// If the above call fails you can look at rgStatus to identify which column's binding info
// was invalid. When the call succeeds, hAccessor is a valid accessor handle which can be
// used as input to IRowset::GetData(), IRowsetChange::SetData() and IRowsetChange::InsertRow()
// When you are done with the accessor handle, release it.
pAccessor->ReleaseAccessor( hAccessor, 0 );
pAccessor->Release();