Click here to Skip to main content
15,885,366 members
Articles / Desktop Programming / MFC

Creating an OLE DB Data Provider

Rate me:
Please Sign up or sign in to vote.
3.45/5 (13 votes)
12 Jan 20029 min read 128.1K   3.4K   69  
This article shows how to create an OLE DB Data Provider that wraps both a C struct and C++ class containing data that is to be made accessible by a SQL query.
#ifndef __I_COLUMNS_ROWSET_IMPL__INCLUDED__
#define __I_COLUMNS_ROWSET_IMPL__INCLUDED__

#include <atlcom.h>
#include <atldb.h>
#include <comutil.h>

#define PROVIDER_COLUMN_ENTRY_DBID(name, dbid, ordinal, member) \
{ \
	(LPOLESTR)OLESTR(name), \
	(ITypeInfo*)NULL, \
	(ULONG)ordinal, \
	DBCOLUMNFLAGS_ISFIXEDLENGTH, \
   (ULONG)sizeof(((_Class*)0)->member), \
	_GetOleDBType(((_Class*)0)->member), \
	(BYTE)0, \
	(BYTE)0, \
	{ \
		EXPANDGUID(dbid.uGuid.guid), \
		(DWORD)dbid.eKind, \
      (LPOLESTR)dbid.uName.ulPropid\
	}, \
   offsetof(_Class, member) \
},


class CColumnsRowsetRow
{
public:

	WCHAR    m_DBCOLUMN_IDNAME[129];
	GUID     m_DBCOLUMN_GUID;
   ULONG    m_DBCOLUMN_PROPID;
   WCHAR    m_DBCOLUMN_NAME[129];
   ULONG    m_DBCOLUMN_NUMBER;
   USHORT   m_DBCOLUMN_TYPE;
   IUnknown *m_DBCOLUMN_TYPEINFO;
   ULONG    m_DBCOLUMN_COLUMNSIZE;
   USHORT   m_DBCOLUMN_PRECISION;
   USHORT   m_DBCOLUMN_SCALE;
   ULONG    m_DBCOLUMN_FLAGS;
   WCHAR    m_DBCOLUMN_BASECOLUMNNAME[129];
   WCHAR    m_DBCOLUMN_BASETABLENAME[129];   
   BOOL     m_DBCOLUMN_KEYCOLUMN;
   
	CColumnsRowsetRow()
	{
		ClearMembers();
	}

	void ClearMembers()
	{
      m_DBCOLUMN_IDNAME[0] = NULL;
      m_DBCOLUMN_GUID = GUID_NULL;
      m_DBCOLUMN_PROPID = 0;
      m_DBCOLUMN_NAME[0] = 0;
      m_DBCOLUMN_NUMBER = 0;
      m_DBCOLUMN_TYPE = 0;
      m_DBCOLUMN_TYPEINFO = 0;
      m_DBCOLUMN_COLUMNSIZE = 0;
      m_DBCOLUMN_PRECISION = 0;
      m_DBCOLUMN_SCALE = 0;
      m_DBCOLUMN_FLAGS = 0;
      m_DBCOLUMN_BASECOLUMNNAME[0] = NULL;
      m_DBCOLUMN_BASETABLENAME[0] = NULL;
      m_DBCOLUMN_KEYCOLUMN = FALSE;
   }


BEGIN_PROVIDER_COLUMN_MAP(CColumnsRowsetRow)
	PROVIDER_COLUMN_ENTRY_DBID("DBCOLUMN_IDNAME", DBCOLUMN_IDNAME, 1, m_DBCOLUMN_IDNAME)
   PROVIDER_COLUMN_ENTRY_DBID("DBCOLUMN_GUID", DBCOLUMN_GUID, 2, m_DBCOLUMN_GUID)
   PROVIDER_COLUMN_ENTRY_DBID("DBCOLUMN_PROPID", DBCOLUMN_PROPID, 3, m_DBCOLUMN_PROPID)
   PROVIDER_COLUMN_ENTRY_DBID("DBCOLUMN_NAME", DBCOLUMN_NAME, 4, m_DBCOLUMN_NAME)
   PROVIDER_COLUMN_ENTRY_DBID("DBCOLUMN_NUMBER", DBCOLUMN_NUMBER, 5, m_DBCOLUMN_NUMBER)
   PROVIDER_COLUMN_ENTRY_DBID("DBCOLUMN_TYPE", DBCOLUMN_TYPE, 6, m_DBCOLUMN_TYPE)
   PROVIDER_COLUMN_ENTRY_DBID("DBCOLUMN_TYPEINFO", DBCOLUMN_TYPEINFO, 7, m_DBCOLUMN_TYPEINFO)
   PROVIDER_COLUMN_ENTRY_DBID("DBCOLUMN_COLUMNSIZE", DBCOLUMN_COLUMNSIZE, 8, m_DBCOLUMN_COLUMNSIZE)
   PROVIDER_COLUMN_ENTRY_DBID("DBCOLUMN_PRECISION", DBCOLUMN_PRECISION, 9, m_DBCOLUMN_PRECISION)
   PROVIDER_COLUMN_ENTRY_DBID("DBCOLUMN_SCALE", DBCOLUMN_SCALE, 10, m_DBCOLUMN_SCALE)
   PROVIDER_COLUMN_ENTRY_DBID("DBCOLUMN_FLAGS", DBCOLUMN_FLAGS, 11, m_DBCOLUMN_FLAGS)
   PROVIDER_COLUMN_ENTRY_DBID("DBCOLUMN_BASECOLUMNNAME", DBCOLUMN_BASECOLUMNNAME, 12, m_DBCOLUMN_BASECOLUMNNAME)
   PROVIDER_COLUMN_ENTRY_DBID("DBCOLUMN_BASETABLENAME", DBCOLUMN_BASETABLENAME, 13, m_DBCOLUMN_BASETABLENAME)
   PROVIDER_COLUMN_ENTRY_DBID("DBCOLUMN_KEYCOLUMN", DBCOLUMN_KEYCOLUMN, 14, m_DBCOLUMN_KEYCOLUMN)
END_PROVIDER_COLUMN_MAP()
};


template <class T, class CreatorClass>
class ATL_NO_VTABLE IColumnsRowsetImpl : public IColumnsRowset
{
   public:

      class CColumnsRowsetRowset : 
	   public CRowsetImpl< CColumnsRowsetRowset , CColumnsRowsetRow, CreatorClass>
      {
         public:

            HRESULT PopulateRowset(IColumnsRowsetImpl *pRowset, ULONG numCols, DBCOLUMNINFO *pColInfo)
            {
               for (ULONG i = 0 ; i < numCols; i++)
               {
                  // copy data out of the pColInfo struct and into the 
                  // rowset array 

                  CColumnsRowsetRow data;

                  // Should select the name correctly, rather than just assuming we'll use the name and
                  // not the guid and propid..

                  lstrcpynW(data.m_DBCOLUMN_IDNAME, pColInfo[i].pwszName , sizeof(data.m_DBCOLUMN_IDNAME));
	               data.m_DBCOLUMN_GUID = GUID_NULL;
                  data.m_DBCOLUMN_PROPID = 0;
                  
                  lstrcpynW(data.m_DBCOLUMN_NAME, pColInfo[i].pwszName , sizeof(data.m_DBCOLUMN_NAME));
                  data.m_DBCOLUMN_NUMBER = pColInfo[i].iOrdinal;
                  data.m_DBCOLUMN_TYPE = pColInfo[i].wType;
                  data.m_DBCOLUMN_TYPEINFO = pColInfo[i].pTypeInfo;
                  data.m_DBCOLUMN_COLUMNSIZE = pColInfo[i].ulColumnSize;
                  data.m_DBCOLUMN_PRECISION = pColInfo[i].bPrecision;
                  data.m_DBCOLUMN_SCALE = pColInfo[i].bScale;
                  data.m_DBCOLUMN_FLAGS = pColInfo[i].dwFlags;
                  lstrcpynW(data.m_DBCOLUMN_BASECOLUMNNAME, pColInfo[i].pwszName, sizeof(data.m_DBCOLUMN_BASECOLUMNNAME));
                  lstrcpynW(data.m_DBCOLUMN_BASETABLENAME, pRowset->GetTableName(), sizeof(data.m_DBCOLUMN_BASETABLENAME));
                  data.m_DBCOLUMN_KEYCOLUMN = (pColInfo[i].iOrdinal == 0) || (pColInfo[i].dwFlags & DBCOLUMNFLAGS_ISROWID);

                  // Base the keycolumn decision on the flags too?
                  // Keycolumn is only used if the column has been displayed :( Pity as this renders the bookmarks
                  // useless..


                  if (!m_rgRowData.Add(data))
                  {
			            return E_OUTOFMEMORY;
                  }
               }

               return S_OK;
            }
      };

      STDMETHOD(GetAvailableColumns)(
         ULONG *pcOptColumns,
         DBID **prgOptColumns)
      {
		   ATLTRACE2(atlTraceDBProvider, 0, "IColumnsRowsetImpl::GetAvailableColumns()\n");

         if (!pcOptColumns || !prgOptColumns)
         {
            return E_INVALIDARG;
         }

         const ULONG c_numOptColumns = 3;

         *pcOptColumns = c_numOptColumns;

         DBID *pOptCols = (DBID*)CoTaskMemAlloc(sizeof(DBID) * c_numOptColumns);

         memset(pOptCols, 0, sizeof(DBID) * c_numOptColumns);

         pOptCols[0] = DBCOLUMN_BASETABLENAME;
         pOptCols[1] =  DBCOLUMN_BASECOLUMNNAME;
         pOptCols[2] =  DBCOLUMN_KEYCOLUMN;
      
         *pcOptColumns = c_numOptColumns;
         *prgOptColumns = pOptCols;

         return S_OK;
      }

      STDMETHOD(GetColumnsRowset)(
         IUnknown *pUnkOuter,
         ULONG cOptColumns,
         const DBID rgOptColumns[],
         REFIID riid,
         ULONG cPropertySets,
         DBPROPSET rgPropertySets[],
         IUnknown **ppColRowset)
      {
		   ATLTRACE2(atlTraceDBProvider, 0, "IColumnsRowsetImpl::GetColumnsRowset()\n");

         // need to create our columns rowset, 
         // then populate it from the actual rowset that we represent...
      
         // We can do that by using IColumnsInfo...

         CColumnsRowsetRowset *pColRowset = 0;

         HRESULT hr = CreateRowset(
            pUnkOuter, 
            riid, 
            cPropertySets, 
            rgPropertySets,
            pColRowset,
            ppColRowset);

         if (SUCCEEDED(hr))
         {
            if (pColRowset)
            {
               T *pT = (T*)this;

               CComQIPtr<IColumnsInfo> spColumnsInfo = pT->GetUnknown();

               if (spColumnsInfo)
               {
                  ULONG numCols = 0;
                  DBCOLUMNINFO *pColInfo = 0;
                  OLECHAR *pNotUsed = 0;

                  hr = spColumnsInfo->GetColumnInfo(&numCols, &pColInfo, &pNotUsed);

                  if (pNotUsed)
                  {
                     CoTaskMemFree(pNotUsed);
                  }

                  if (SUCCEEDED(hr))
                  {
                     hr = pColRowset->PopulateRowset(this, numCols, pColInfo);
                  }

                  CoTaskMemFree(pColInfo);
               }
            }
            else
            {
               hr = E_UNEXPECTED;
            }
         }
      
         return hr;
      }

	_bstr_t GetTableName()
	{
		return m_wsTableName;
	}

	void SetTableName( WCHAR* wsTableName )
	{
		m_wsTableName = wsTableName;
	}
   
	private :

      HRESULT CreateRowset(
         IUnknown * pUnkOuter,	
	      REFIID riid,				
         ULONG cPropertySets,
         DBPROPSET rgPropertySets[],
         CColumnsRowsetRowset *&pRowsetObj,
	      IUnknown **ppRowset)
      {
	      HRESULT hr;

         T* pT = (T*)this;

	      if (ppRowset != NULL)
         {
		      *ppRowset = NULL;
         }

	      if ((pUnkOuter != NULL) && !InlineIsEqualUnknown(riid))
         {
		      return DB_E_NOAGGREGATION;
         }

	      CComPolyObject<CColumnsRowsetRowset>* pPolyObj;
	      
         if (FAILED(hr = CComPolyObject<CColumnsRowsetRowset>::CreateInstance(pUnkOuter, &pPolyObj)))
         {
		      return hr;
         }
	      
         // Ref the created COM object and Auto release it on failure
	      
         CComPtr<IUnknown> spUnk;
	      
         hr = pPolyObj->QueryInterface(&spUnk);
	      
         if (FAILED(hr))
	      {
		      delete pPolyObj; // must hand delete as it is not ref'd
		      return hr;
	      }
	      
         // Get a pointer to the Rowset instance
	      pRowsetObj = &(pPolyObj->m_contained);

	      if (FAILED(hr = pRowsetObj->FInit(pT)))
         {
		      return hr;
         }

         // Set Properties that were passed in.

         const GUID* ppGuid[1];
         ppGuid[0] = &DBPROPSET_ROWSET;

         // Call SetProperties.  The true in the last parameter indicates
         // the special behavior that takes place on rowset creation (i.e.
         // it succeeds as long as any of the properties were not marked
         // as DBPROPS_REQUIRED.

         hr = pRowsetObj->SetProperties(0, cPropertySets, rgPropertySets, 1, ppGuid, true);

         if (FAILED(hr))
         {
            return hr;
         }

	      pRowsetObj->SetSite(pT->GetUnknown());

	      if (InlineIsEqualGUID(riid, IID_NULL) || ppRowset == NULL)
	      {
		      if (ppRowset != NULL)
			      *ppRowset = NULL;
		      return hr;
	      }

		   if (InlineIsEqualGUID(riid, IID_NULL) || ppRowset == NULL)
		   {
			   if (ppRowset != NULL)
				   *ppRowset = NULL;
			   return hr;
		   }
		   hr = pPolyObj->QueryInterface(riid, (void**)ppRowset);
		   if (FAILED(hr))
			   return hr;
/*		   for (int iBind = 0; iBind < pT->m_rgBindings.GetSize(); iBind++)
		   {
			   T::_BindType* pBind = NULL;
			   T::_BindType* pBindSrc = NULL;
			   ATLTRY(pBind = new T::_BindType);
			   if (pBind == NULL)
			   {
				   ATLTRACE2(atlTraceDBProvider, 0, "Failed to allocate memory for new Binding\n");
				   return E_OUTOFMEMORY;
			   }
			   // auto cleanup on failure
			   CAutoMemRelease<T::_BindType> amr(pBind);
			   pBindSrc = pT->m_rgBindings.GetValueAt(iBind);
			   if (pBindSrc == NULL)
			   {
				   ATLTRACE2(atlTraceDBProvider, 0, "The map appears to be corrupted, failing!!\n");
				   return E_FAIL;
			   }
			   if (!pRowsetObj->m_rgBindings.Add(pT->m_rgBindings.GetKeyAt(iBind), pBind))
			   {
				   ATLTRACE2(atlTraceDBProvider, 0, "Failed to add hAccessor to Map\n");
				   return E_OUTOFMEMORY;
			   }
			   if (pBindSrc->cBindings)
			   {
				   ATLTRY(pBind->pBindings = new DBBINDING[pBindSrc->cBindings])
				   if (pBind->pBindings == NULL)
				   {
					   ATLTRACE2(atlTraceDBProvider, 0, "Failed to Allocate dbbinding Array\n");
					   // We added it, must now remove on failure
					   pRowsetObj->m_rgBindings.Remove(pT->m_rgBindings.GetKeyAt(iBind));
					   return E_OUTOFMEMORY;
				   }
			   }
			   else
			   {
				   pBind->pBindings = NULL; // NULL Accessor
			   }

			   pBind->dwAccessorFlags = pBindSrc->dwAccessorFlags;
			   pBind->cBindings = pBindSrc->cBindings;
			   pBind->dwRef = 1;
			   memcpy (pBind->pBindings, pBindSrc->pBindings, (pBindSrc->cBindings)*sizeof(DBBINDING));
			   pBind = amr.Detach();
		   }
*/
	      return hr;
      }

	_bstr_t		m_wsTableName;
};

#endif // __I_COLUMNS_ROWSET_IMPL__INCLUDED__

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United States United States
David Utz
Senior Software Engineer
Analytical Graphics, Inc.
40 General Warren Blvd.
Malvern, PA 19355
dutz@stk.com

Comments and Discussions