Resources

OLE DB Format Processing:
Overriding Formats for Input Operations

In a SAS data set, a variable (column) can have persisted formatting information. To use the default format, you create an instance of SASFORMAT, set the wType member to SASFMT_FORMAT, and set the remaining members to zero (0) or NULL, as appropriate. To override the default format values, you can specify a format length (width) and number of decimals (for numeric formats).

Note: For more information about using the default format, see Applying Default Formats to Input Operations.


Format Override Example

The example data set has three columns: SALEDATE, QUANTITY, and PRICE. These are the same sample columns that were used in the topic Applying Default Formats to Input Operations. However, in this case, the objective is to return SALEDATE using MMDDYY8. formatting and to return PRICE using DOLLAR8.2. formatting. In addition, there may be a need for the application to do some calculations on the PRICE column, so the example also binds to that column as a numeric value. As a result, this example requires the following four bindings (even though there are only three columns):

  1. SALEDATE bound as DBTYPE_BSTR with MMDDYY8. formatting
  2. QUANTITY bound as a DBTYPE_R8
  3. PRICE bound as DBTYPE_BSTR with DOLLAR8.2 formatting
  4. PRICE bound as DBTYPE_R8

The following code will create an array of DBBINDING structures.

DBBINDING rgBindings[4];// Creates four bindings. 
DBBINDEXT rgBindingExtensions[2]; // Two bindings have 
SASFORMAT rgFormatOverrides[2];   // formatting overrides.
ULONG dwOffset; // Used to track the offset of a bound data point into an I/O buffer.

As each binding is created, the following struct will be used to lay out a status and a value part.

typedef struct  
{              
DWORD status;  
BYTE value[1];
} COLUMNDEF;

As a matter of good programming, all structures are initialized, too.

memset( rgBindings, 0, sizeof( rgBindings ) );
memset( rgBindingExtensions, 0, sizeof( rgBindingExtensions ) );
memset( rgFormatOverrides, 0, sizeof( rgFormatOverrides ) );

Parts are laid into the buffer at offset 0. When the bindings are created, dwOffset should be the total number of bytes needed to hold one row in memory.

dwOffset = 0;

SALEDATE is bound as DBTYPE_BSTR with the MMYYDD8. format applied. The basic OLE DB binding information comes first.

rgBindings[0].iOrdinal = 1;
rgBindings[0].dwPart = DBPART_VALUE | DBPART_STATUS;
rgBindings[0].obValue =dwOffset + offsetof( COLUMNDEF, value );
rgBindings[0].obStatus =dwOffset + offsetof( COLUMNDEF, status );
rgBindings[0].dwMemOwner = DBMEMOWNER_CLIENTOWNED;
rgBindings[0].wType = DBTYPE_BSTR;

The format override is set up for this column.

rgFormatOverrides[0].wType = SASFMT_FORMAT;
rgFormatOverrides[0].pwszName = L"MMYYDD";
rgFormatOverrides[0].iLength = 8;

The SASFORMAT struct is linked to the DBBINDING struct.

rgBindingExtensions[0].pExtension = (BYTE *) &(rgFormatOverrides[0]);
rgBindingExtensions[0].ulExtension = 1;
rgBindings[0].pBindExt = &(rgBindingExtensions[0]);

The offset is set to increase past the data points for this column.

dwOffset += sizeof(BSTR) + offsetof( COLUMNDATA, value );

QUANTITY is bound as DBTYPE_R8. This case is simple because only basic OLE DB binding information is needed.

rgBindings[1].iOrdinal = 2;
rgBindings[1].dwPart = DBPART_VALUE | DBPART_STATUS;
rgBindings[1].obValue =dwOffset + offsetof( COLUMNDEF, value );
rgBindings[1].obStatus =dwOffset + offsetof( COLUMNDEF, status );
rgBindings[1].wType = DBTYPE_R8;
dwOffset += sizeof(double) + offsetof( COLUMNDATA, value );

PRICE is bound as DBTYPE_BSTR with the DOLLAR8.2 format applied. This is similar to the first binding that was created.

rgBindings[2].iOrdinal = 3;
rgBindings[2].dwPart = DBPART_VALUE | DBPART_STATUS;
rgBindings[2].obValue =dwOffset + offsetof( COLUMNDEF, value );
rgBindings[2].obStatus =dwOffset + offsetof( COLUMNDEF, status );
rgBindings[2].dwMemOwner = DBMEMOWNER_CLIENTOWNED;
rgBindings[2].wType = DBTYPE_BSTR;

rgFormatOverrides[1].wType = SASFMT_FORMAT;
rgFormatOverrides[1].pwszName = L"DOLLAR";
rgFormatOverrides[1].iLength = 8;
rgFormatOverrides[1].iDecimals = 2;

rgBindingExtensions[1].pExtension = (BYTE *) &(rgFormatOverrides[0]);
rgBindingExtensions[1].ulExtension = 1;
rgBindings[2].pBindExt = &(rgBindingExtensions[0]);
dwOffset += sizeof(BSTR) + offsetof( COLUMNDATA, value );

PRICE is bound as DBTYPE_R8 .

rgBindings[3].iOrdinal = 3;
rgBindings[3].dwPart = DBPART_VALUE | DBPART_STATUS;
rgBindings[3].obValue =dwOffset + offsetof( COLUMNDEF, value );
rgBindings[3].obStatus =dwOffset + offsetof( COLUMNDEF, status );
rgBindings[3].wType = DBTYPE_R8;

After executing the following code, dwOffset will be the number of bytes that the application will need to store a row of data when that row of data is retrieved using this binding information.

dwOffset += sizeof(double) + offsetof( COLUMNDATA, value );

See Also:

About Format and Informat Processing with OLE DB
Using the SASFORMAT Structure
Applying Default Formats to Input Operations
Applying Informats to Output Operations