MDAC 2.5 SDK - OLE DB Programmer's Reference
Chapter 27: OLE DB for OLAP Code Samples


 

Creating a Dataset and Getting Cell Data

MDPDataset is used to get the cell data for an MDX query specified by pwszQuery, compute the cell data coordinates, and call IMDDataset::GetCellData for each cell in the dataset.

//
HRESULT MDPDataset(ICommandText *pICommandText, pwszQuery)
{
   HRESULT hr;

   struct COLUMNDATA
   {
      SDWORD dwLength;     // length of data (not space allocated)
      DBSTATUS dwStatus;   // status of column
      VARIANT var;         // variant value
   };
   // Execute the MDX query.
   IMDDataset* pIMDDataset = NULL;
   hr = pICommandText->SetCommandText(DBGUID_MDX, pwszQuery);
   hr = pICommandText->Execute(NULL, IID_IMDDataset, NULL, NULL, 
                               (IUnknown **)&pIMDDataset ));

   // Fetch the axis info to compute the coordinates.
   ULONG cAxis;
   MDAXISINFO* rgAxisInfo = NULL;
   hr = pIMDDataset->GetAxisInfo( &cAxis, &rgAxisInfo );

   // Calculate the coordinate "offset factors". These are used to 
   // translate from a point in cell space to a set of axis coordinates.
   // Example, consider a dataset with three axes and the following 
   // number of coordinates for each axis:
   //   {x,y,z} = {3,4,5}
   //      rgAxisOffset[2] = 3*4
   //      rgAxisOffset[1] = 3
   //      rgAxisOffset[0] = 1
   // Thus, where p represents the cell's ordinal value:
   //      z = p % 12
   //      y = (p - z*12) % 3
   //      x = (p - z*12 - y*3)
   // And,
   //      p = x + 3*y + 12*z

   ULONG cAxisTuple;
   ULONG iOffset = 1;
   ULONG ulMaxCoord = 0;
   ULONG rgAxisOffset[MAX_DIMS];   // array of offset multipliers

   // For all axes, excluding slicer axis...
   for (ULONG iAxis=0; iAxis < cAxis-1; iAxis++)
   {
      rgAxisOffset[iAxis] = iOffset;
      cAxisTuple = rgAxisInfo[iAxis].cCoordinates;
      iOffset *= cAxisTuple;
   }
   ulMaxCoord = iOffset;
   ULONG cSliceCoord = rgAxisOffset[cAxis-1]

   // Bind to the column values for each cell.
   IColumnsInfo *pIColumnsInfo = NULL;
   hr = pIMDDataset->QueryInterface(IID_IColumnsInfo, 
      (void**)&pIColumnsInfo);

   ULONG cCol;
   WCHAR* pStringsBuffer = NULL;
   DBCOLUMNINFO* pInfo = NULL;
   hr = pIColumnsInfo->GetColumnInfo(&cCol, &pInfo, &pStringsBuffer);

   // Create bindings for each cell's columns, ordered as returned by 
   // GetColumnInfo. Bind everything as Variant.
   dwOffset = 0;
   ULONG iBind = 0;
   ULONG cBind = 0;
   for (ULONG iCol=0; iCol < cCol; iCol++)
   {
      rgBind[iBind].iOrdinal    = pInfo[iCol].iOrdinal;
      rgBind[iBind].obValue     = dwOffset + 
         offsetof(COLUMNDATA,var);
      rgBind[iBind].obLength    = dwOffset + 
         offsetof(COLUMNDATA,dwLength);
      rgBind[iBind].obStatus    = dwOffset + 
         offsetof(COLUMNDATA,dwStatus);
      rgBind[iBind].pTypeInfo   = NULL;
      rgBind[iBind].pObject     = NULL;
      rgBind[iBind].pBindExt    = NULL;
      rgBind[iBind].cbMaxLen    = sizeof(VARIANT);
      rgBind[iBind].dwFlags     = 0;
      rgBind[iBind].eParamIO    = DBPARAMIO_NOTPARAM;
      rgBind[iBind].dwPart      = DBPART_VALUE | 
                                  DBPART_LENGTH | 
                                  DBPART_STATUS;
      rgBind[iBind].dwMemOwner  = DBMEMOWNER_CLIENTOWNED;
      rgBind[iBind].wType       = DBTYPE_VARIANT;
      rgBind[iBind].bPrecision  = 0;
      rgBind[iBind].bScale      = 0;

      dwOffset += rgBind[iBind].cbMaxLen + offsetof(COLUMNDATA,var);
      iBind++;
   }
   cBind = iBind;

   // Create the accessor.
   IAccessor* pIAccessor;
   hr = pIMDDataset->QueryInterface(IID_IAccessor, void**)&pIAccessor);
   // Note that the value of dwOffset contains the size of a cell.
   // Failure to specify this value will result in an error.
   hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, cBind, rgBind, 
                                   dwOffset, phAccessor, NULL);

   pIAccessor->Release();
   pIColumnsInfo->Release();

   // Allocate a buffer for a single cell in a slice.
   ULONG cbRowsize = dwOffset;
   pData = (BYTE*)CoTaskMemAlloc(cbRowSize);

   // Fetch each cell in the dataset.
   for (ULONG ulCellCoord=0; ulCellCoord < ulMaxCoord; ulCellCoord++)
   {
      // Populate cell buffer.
      hr = pIMDDataset->GetCellData(hAccessor, ulCellCoord, ulCellCoord, 
                                    pData);

      // Traverse each bound cell property value for a single cell "row."
      // Use pColumn to access each column's data values.
      for (iBind=0; iBind < cBind; iBind++)
      {
         // Advance to the column value.
         (COLUMNDATA*)pColumn = (COLUMNDATA *)(pData + 
                                 rgBind[iBind].obLength);
         // pColumn->var points to the variant value.
      }
   }
   // Free the accessor and rowset.
   hr = pIAccessor->ReleaseAccessor(hAccessor, NULL);
   hr = pIAccessor->Release();
   hr = pIColumnsInfo->Release();

   // Free the row data and bindings.
   CoTaskMemFree(pData);
   CoTaskMemFree(rgBind);

   // Free the column info.
   CoTaskMemFree(pInfo);
   CoTaskMemFree(pwszStringsBuffer);

   hr = pIMDDataset->FreeAxisInfo(cAxis, rgAxisInfo);
   hr = pIMDDataset->Release();
   return hr;
}