Creating a Dataset and Obtaining Axis Information

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

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

   struct COLUMNDATA
   {
      DWORD     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.
   DBCOUNTITEM cAxis;
   MDAXISINFO* rgAxisInfo = NULL;
   hr = pIMDDataset->GetAxisInfo( &cAxis, &rgAxisInfo );

   for (DBCOUNTITEM iAxis=0; iAxis < cAxis; iAxis++)
   {
      // rgAxisInfo contains the array of dimensions for each axis.
      for (DBCOUNTITEM iDim=0; iDim <  rgAxisInfo[iAxis].cDimensions; iDim++)
      {
         // MDAXISINFO_GETAT(rgAxisInfo, iAxis).rgpwszDimensionNames points
         // to the dimension name.
         assert( MDAXISINFO_GETAT(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);

      DBCOUNTITEM 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, and skip DBTYPE_VECTOR
      // type columns.
      DBBYTEOFFSET dwOffset = 0;
      DBCOUNTITEM iBind = 0;
      DBCOUNTITEM cBind = 0;
      DBBINDING* rgBind = (DBBINDING*)CoTaskMemAlloc(
                           cCol*sizeof(DBBINDING));
      for (DBCOUNTITEM 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.
      DBLENGTH 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.
         DBCOUNTITEM cRowsObtained;
         hr = pIRowset->GetNextRows(NULL, 0, 20, &cRowsObtained, &pRows);

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

         for (DBCOUNTITEM 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;
}