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.
|
|
Set this member to 0x00.
This value indicates to return regular text from the provider.
|
|
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].dwFlags = 0x00;
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].dwFlags = 0x00;
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].dwFlags = 0x00;
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].dwFlags = 0x00;
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();