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, LPCOLESTR pwszQuery)
{
   HRESULT hr;

   struct COLUMNDATA
   {
      DWORD 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.
   DBCOUNTITEM 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

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


   // For all axes, excluding the slicer axis if present.
   for (DBCOUNTITEM iAxis=0; iAxis < cAxis; iAxis++)
   {
      // Skip the slicer axis.
      if (rgAxisInfo[iAxis].iAxis == MDAXIS_SLICERS) continue; 
      rgAxisOffset[iAxis] = iOffset;
      cAxisTuple = rgAxisInfo[iAxis].cCoordinates;
      iOffset *= cAxisTuple;
   }
   ulMaxCoord = iOffset;

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

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

   // Allocate size for rgBind
   DBBINDING* rgBind;
   rgBind = (DBBINDING*)CoTaskMemAlloc(cCol * sizeof(DBBINDING));

   // Create bindings for each cell's columns, ordered as returned by
   // GetColumnInfo. Bind everything as Variant.
   DWORD dwOffset = 0;
   DBCOUNTITEM iBind = 0;
   DBCOUNTITEM cBind = 0;
   for (DBORDINAL 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.
   DBLENGTH cbRowsize = dwOffset;
   pData = (BYTE*)CoTaskMemAlloc(cbRowSize);

   // Fetch each cell in the dataset.
   for (DBORDINAL 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.
         ::VariantClear(&(pColumn->var));
      }
   }
   // 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;
}