HOWTO: Using OLE DB DBTYPE_VARNUMERIC

ID: Q229884


The information in this article applies to:


SUMMARY

When using OLE DB with databases that support very large numeric types (for example, Oracle NUMBER, or SQL Server DECIMAL or NUMERIC, and so forth) OLE DB returns a datatype of DBTYPE_NUMERIC. These large numbers can be stored and retrieved using the DB_VARNUMERIC structure provided in OLEDB.h. The following code illustrates how to retrieve these numbers and store them in a C double.


   IColumnsInfo * pIColumnsInfo;
   ULONG cColumns;
   DBCOLUMNINFO * prgInfo;
   OLECHAR * pStringsBuffer;
   IMalloc * pIMalloc;
   int prec;
   int scale;
   int sign;
   BYTE * buffer;
   double dValue = 0;
   BYTE CalcVal;

   pICommandText->Execute( NULL, IID_IRowset, NULL,
		& cRowsAffected, ( IUnknown ** ) & pIRowset );

   pICommandText->Release();

   pIRowset->QueryInterface( IID_IColumnsInfo, ( void ** )
		& pIColumnsInfo );

   pIColumnsInfo->GetColumnInfo( & cColumns, & prgInfo, 
		& pStringsBuffer );

   // For the purposes of this example the table contains 
   // only 1 column of type NUMBER.
   // Tthe prgInfo[0].pTypeInfo will be DBTYPE_VARNUMERIC == 139

   DBBindings[0].iOrdinal = prgInfo[0].iOrdinal;
   DBBindings[0].obValue = 0;
   DBBindings[0].obLength = 0;
   DBBindings[0].obStatus = 0;
   DBBindings[0].pTypeInfo = NULL;
   DBBindings[0].pObject = NULL;
   DBBindings[0].pBindExt = NULL;
   DBBindings[0].dwPart = DBPART_VALUE;
   DBBindings[0].dwMemOwner = DBMEMOWNER_CLIENTOWNED;
   DBBindings[0].eParamIO = DBPARAMIO_NOTPARAM;
   DBBindings[0].cbMaxLen = prgInfo[0].ulColumnSize;
   DBBindings[0].dwFlags = prgInfo[0].dwFlags;
   DBBindings[0].wType = prgInfo[0].wType;
   DBBindings[0].bPrecision = prgInfo[0].bPrecision;
   DBBindings[0].bScale = prgInfo[0].bScale;

   // Add 3 extra bytes to the data length for 
   // precision, scale, and sign.
   buffer = new BYTE[ prgInfo[0].ulColumnSize + 3 ];
   memset( buffer, 0, prgInfo[0].ulColumnSize + 3 );

   pIRowset->QueryInterface( IID_IAccessor, ( void ** )
		& pIAccessor );

   pIAccessor->CreateAccessor( DBACCESSOR_ROWDATA, 
		1, 
		DBBindings, 
		prgInfo[0].ulColumnSize, 
		& hAccessor, 
		DBBindStatus );

   pIRowset->GetNextRows( NULL, 0, 1, & cRowsObtained, 
		& prghRows );

   hr = pIRowset->GetData( rghRows, hAccessor, buffer );

   prec = ( int ) ( ( DB_VARNUMERIC * ) buffer )->precision;
   scale = ( int ) ( ( DB_VARNUMERIC * ) buffer )->scale;
   sign = ( int ) ( ( DB_VARNUMERIC * ) buffer )->sign; 
When the data is retrieved with IRowset->GetData() it is placed in the buffer and will be in the standard numeric format. However, it will just be very large; too large to access as a double or integer. It will be necessary to process the data and move it to another datatype as in the preceding code.

The following algorithm can be used to move the value into a variable of type double:

   for( ULONG i = 0, j = 0 ; i < prgInfo[0].ulColumnSize ; i++, j+=2 )
   {
   CalcVal = ( ( DB_VARNUMERIC * ) buffer )->val[ i ];
	CalcVal <<= 4;
		CalcVal >>= 4;
		if( i > 0 )
			dValue += CalcVal * pow(16, j );
		else
			dValue += CalcVal;

		CalcVal = ( ( DB_VARNUMERIC * ) buffer )->val[ i ];
		CalcVal >>= 4;
		dValue += CalcVal * pow(16, j + 1);
   } 


MORE INFORMATION

DB_VARNUMERIC is declared as follows:


typedef struct  tagDB_VARNUMERIC
    {
    BYTE precision;
    SBYTE scale;
    BYTE sign;
    BYTE val[ 1 ];
    }	DB_VARNUMERIC; 
Obviously, the structures member variable val[1] is not large enough to hold a large number. Therefore, you can declare a BYTE array and cast the array as a DB_VARNUMERIC structure to access the data members.

Additional query words: OLEDB C++ NUMBER NUMERIC DECIMAL


Keywords          : kbMDAC kbOLEDB 
Version           : WINDOWS:1.5,2.0,2.1,2.1 SP1,2.1 SP2,2.5
Platform          : WINDOWS 
Issue type        : kbhowto 

Last Reviewed: May 13, 1999