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;
}