Creating a Dataset and Obtaining Axis Information

This function executes an MDX query statement specified by pwszQuery. GetAxisInfo and GetAxisRowset are called to traverse the column information for each axis in the MDX query.

HRESULT MDPQueryColumnInfo(ICommandText *pICommandText, pwszQuery)
{
   HRESULT hr;

   struct COLUMNDATA
   {
      SDWORD   dwLength;      // length of data
      DBSTATUS   dwStatus;      // status of column
      SHORT      wPadding;
      BYTE      bData[1];      // variable length data 
   };

   // Execute the query
   IMDDataset* pIMDDataset = NULL;
   hr = pICommandText->SetCommandText(DBGUID_MDX, pwszQuery);
   hr = pICommandText->Execute(NULL, IID_IMDDataset, NULL, NULL,
                               (IUnknown **)&pIMDDataset ));

   // Fetch and traverse the axis info
   ULONG cAxis;
   MDAXISINFO* rgAxisInfo = NULL;
   hr = pIMDDataset->GetAxisInfo( &cAxis, &rgAxisInfo );

   for (ULONG iAxis=0; iAxis < cAxis; iAxis++)
   {
      // rgAxisInfo contains the array of dimensions for each axis
      for (ULONG iDim=0; iDim < 
         rgAxisInfo[iAxis].cDimensions; iDim++)
      {
         // rgAxisInfo[iAxis].rgpwszDimensionNames points 
         // to the dimension name
         assert(rgAxisInfo[iAxis].rgpwszDimensionNames);
      }

      // Fetch the axis rowset for each axis
      IRowset* pIrowset = NULL;
      hr = pIMDDataset->GetAxisRowset(NULL, iAxis, 
                           IID_IRowset, 0, NULL, (IUnknown**)&pIRowset));

      // Fetch the column info for the axis rowset
      IColumnsInfo *pIColumnsInfo = NULL;
      hr = pIRowset->QueryInterface(IID_IColumnsInfo, 
                              (void**)&pIColumnsInfo);

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

      // Create bindings for all columns, in same order as given by 
      // GetColumnInfo().Bind everything as string, skip DBTYPE_VECTOR 
      // type columns
      ULONG dwOffset = 0;
      ULONG iBind = 0;
      ULONG cBind = 0;
      DBBINDING* rgBind = (DBBINDING*)CoTaskMemAlloc(
                              cCol*sizeof(DBBINDING));
      for (ULONG iCol=0; iCol < cCol; iCol++)
      {
         // Skip columns of type _VECTOR (Probably binary data)
         if (pInfo[iCol].wType & DBTYPE_VECTOR)
            continue;

         rgBind[iBind].iOrdinal  = pInfo[iCol].iOrdinal;
         rgBind[iBind].obValue   = dwOffset + 
            offsetof(COLUMNDATA,bData);
         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  = pInfo[iCol].ulColumnSize;
         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].bPrecision= 0;
         rgBind[iBind].bScale    = 0;
         rgBind[iBind].wType     = DBTYPE_STR;

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

      // Create the accessor.
      IAccessor* pIAccessor;
      hr = pIRowset->QueryInterface(IID_IAccessor, (void**)&pIAccessor);
      hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, cBind, rgBind, 
                                      dwOffset, phAccessor, NULL);

      // Allocate a buffer for a single row of data.
      ULONG cbRowsize = dwOffset;
      BYTE* pData = (BYTE *)CoTaskMemAlloc(cbRowSize);

      while (SUCCEEDED(hr))
      {
         // Prepare internal buffers and get handles to // the rows.
         // Fetch 20 rows at a time
         ULONG cRowsObtained;
         hr = pIRowset->GetNextRows(NULL, 0, 20, &cRowsObtained, &pRows);

         // break on EndOfRowset
         if (cRowsObtained == 0)   break;

         for (ULONG iRow=0; iRow < cRowsObtained; iRow++)
         {
            // Clear buffer.
            memset(pData, 0, cbRowSize);

            // Get the row data.
            hr = pIRowset->GetData(rghRows[iRow], hAccessor, pData);

            // traverse each bound column value for a single 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);
               // (WCHAR*)pColumn->bData points to the string value
             }
         }
         // Release the row handles.
         hr = pIRowset->ReleaseRows(cRowsObtained, rghRows, NULL, NULL, 
                                    NULL);
      }
      // Free the accessor and rowset
      hr = pIAccessor->ReleaseAccessor(hAccessor, NULL);
      hr = pIAccessor->Release();
      hr = pIRowset->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;
}