Click here to Skip to main content
15,878,852 members
Please Sign up or sign in to vote.
3.33/5 (3 votes)
See more:
In order to further explore ODBC and increase my programming experience i was going to put together my own ODBC wrapper classes. Below are the wrapper classes i have made so far and i'm curious on any problems anyone happens to see with it :) thanks for the help and cheers

C++
template<class TDerived, SQLSMALLINT THandle = SQL_HANDLE_UNKNOWN>
class __declspec(novtable) CHandle 
{
public:
	SQLHANDLE   m_h;
	SQLSMALLINT m_hType;

	enum { SQL_HANDLE_UNKNOWN = -1 };

public:
	CHandle() : 
		m_h(NULL), m_hType(THandle)
	{
		_ASSERTE(m_hType != -1);
	}

	
	CHandle(SQLHANDLE h) :
		m_h(h), m_hType(THandle)
	{
		_ASSERTE(h != NULL);
		_ASSERTE(m_hType != -1);
		
		Attach(h);
	}

	void Attach(_In_ SQLHANDLE h)
	{
		_ASSERTE(h != NULL);
		_ASSERTE(m_h == NULL);
		_ASSERTE(m_hType != -1);
		
		m_h = h;
	}

	SQLHANDLE Detach()
	{
		_ASSERTE(m_h != NULL);
		_ASSERTE(m_hType != -1);
		SQLHANDLE hTemp = m_h;
		m_h = NULL;
		return hTemp;
	}

	bool Create()
	{
		_ASSERTE(m_h == NULL);
		_ASSERTE(m_hType != -1);
		
		g_rt = SQLAllocHandle(THandle, NULL, &m_h);
		_ASSERTE(SQL_SUCCEEDED(g_rt));
		if (SQL_SUCCEEDED(g_rt) == FALSE)
			return FALSE;
		
		return TRUE;
	}

	void Destroy()
	{
		_ASSERTE(m_h != NULL);
		_ASSERTE(m_hType != -1);
		
		g_rt = SQLFreeHandle(THandle, m_h);
		_ASSERTE(SQL_SUCCEEDED(g_rt));

		m_h = NULL;
		m_hType = -1;
	}

	// TODO : test setting multiple attributes
	bool SetAttribute(_In_ SQLINTEGER attribute, _In_ SQLPOINTER value)
	{
		_ASSERTE(m_h != NULL);

		return static_cast<TDerived*>(this)->_SetAttribute(attribute, value);
	}

	// TODO : test getting multiple attributes
	bool GetAttribute(_In_ SQLINTEGER attribute, _Inout_ SQLPOINTER pValue, _Inout_ SQLINTEGER* pcbWritten)
	{
		_ASSERTE(m_h != NULL);

		return static_cast<TDerived*>(this)->_GetAttribute(attribute, pValue, pcbWritten);
	}

	operator SQLHANDLE()
	{
		return m_h;
	}

};



class __declspec(novtable) CEnviromentHandle : 
	public CHandle< CEnviromentHandle, SQL_HANDLE_ENV>
{
public:


	bool _SetAttribute(_In_ SQLINTEGER attribute, _In_ SQLPOINTER value)
	{
		_ASSERTE(m_h != NULL);

		g_rt = SQLSetEnvAttr(m_h, attribute, value, SQL_NTS);
		_ASSERTE(SQL_SUCCEEDED(g_rt));
		if (SQL_SUCCEEDED(g_rt) == FALSE)
			return FALSE;
		
		return TRUE;
	}

	// TODO : i might need last parameter. For now ignoreing
	bool _GetAttribute(_In_ SQLINTEGER attribute, _Inout_ SQLPOINTER pValue, _Inout_ SQLINTEGER* pcbWritten)
	{
		_ASSERTE(m_h != NULL);

		g_rt = SQLGetEnvAttr(m_h, attribute, pValue, SQL_IS_UINTEGER, pcbWritten);
		_ASSERTE(SQL_SUCCEEDED(g_rt));
		if (SQL_SUCCEEDED(g_rt) == FALSE)
			return FALSE;
		
		return TRUE;
	}
};

class __declspec(novtable) CConnectionHandle : 
	public CHandle< CConnectionHandle, SQL_HANDLE_DBC>
{
public:

	bool _SetAttribute(_In_ SQLINTEGER attribute, _In_ SQLPOINTER value)
	{
		_ASSERTE(m_h != NULL);

		g_rt = SQLSetConnectAttr(m_h, attribute, value, SQL_NTS);
		_ASSERTE(SQL_SUCCEEDED(g_rt));
		if (SQL_SUCCEEDED(g_rt) == FALSE)
			return FALSE;
		
		return TRUE;
	}

	// TODO : i might need last parameter. For now ignoreing
	bool _GetAttribute(_In_ SQLINTEGER attribute, _Inout_ SQLPOINTER pValue, _Inout_ SQLINTEGER* pcbWritten)
	{
		_ASSERTE(m_h != NULL);

		g_rt = SQLGetConnectAttr(m_h, attribute, pValue, SQL_IS_UINTEGER, pcbWritten);
		_ASSERTE(SQL_SUCCEEDED(g_rt));
		if (SQL_SUCCEEDED(g_rt) == FALSE)
			return FALSE;
		
		return TRUE;
	}
};


class __declspec(novtable) CStatementHandle : 
	public CHandle< CStatementHandle, SQL_HANDLE_STMT>
{
public:

	bool _SetAttribute(_In_ SQLINTEGER attribute, _In_ SQLPOINTER value)
	{
		_ASSERTE(m_h != NULL);

		g_rt = SQLSetStmtAttr(m_h, attribute, value, SQL_NTS);
		_ASSERTE(SQL_SUCCEEDED(g_rt));
		if (SQL_SUCCEEDED(g_rt) == FALSE)
			return FALSE;
		
		return TRUE;
	}

	// TODO : i might need last parameter. For now ignoreing
	bool _GetAttribute(_In_ SQLINTEGER attribute, _Inout_ SQLPOINTER pValue, _Inout_ SQLINTEGER* pcbWritten)
	{
		_ASSERTE(m_h != NULL);

		g_rt = SQLGetStmtAttr(m_h, attribute, pValue, SQL_IS_UINTEGER, pcbWritten);
		_ASSERTE(SQL_SUCCEEDED(g_rt));
		if (SQL_SUCCEEDED(g_rt) == FALSE)
			return FALSE;
		
		return TRUE;
	}
};
Posted
Updated 2-Aug-12 22:48pm
v2
Comments
Kenneth Haugland 2-Aug-12 23:09pm    
What Im I A Compiler? USe Visual studio insted :)
v3nOm_ 2-Aug-12 23:20pm    
what? i do use MSVC++ 2012
Kenneth Haugland 2-Aug-12 23:30pm    
I understood that, but what is the problem? Do you want us to *proof read* your code?
v3nOm_ 3-Aug-12 1:43am    
no....i'm wanting to get design advice/suggestions compared to what i have in place. i'm not asking why it wont compile...it compiles fine. I'm wanting to become a better programmer and therefore am looking for suggestions for improvement and opinions.

As suggestions. If you return bool then you have to return false/true instead of FALSE/TRUE, because you are not using BOOL. Second, don't name the class Handle. Instead of CConnectionHandle use CConnection name, or even better CDatabase. Instead ir CStatementHandle use CStatement or CQuery, and so on. And the best is to not expose the handle with HANDLE operator. Detach function is useless. Attach inside constructor is even more useless.
You have to follow ODBC strategic. For instance, when you make a CStatement, it makes sense with a CConnection. A CParameter makes sense only with a CStatement. CColumn makes sence only with CStatement, and so on.
I don't see any ODBC logics there.
Following ODBC logic, CConnection must have function Connect/DriverConnect/ConnectDirect/Disconnect or Open/Close, with DSN or with connection string. CStatement need to have such functions as Exec/ExecDirect, Fetch/Next/Prev/Close/Cancel/Bind. And navigating through columns, need to know result column count, row count, end and finish of result while fetching, errors in case of failing execution, so on.

This code is mostly dead. I don't see how you can use it to connect to a database, execute a query, get the result. But this is exactly why ODBC exists.
 
Share this answer
 
v2
Comments
Volynsky Alex 8-Aug-12 17:15pm    
+5!
My advice is to test the code yourself first, to see if it solves the problem you are addressing.

I've had little need in the last decade, to do much with the ODBC layer. Using Wizard generated code such as MFC generated classes based on CRecordset, save a lot of time, and perform well enough for most operations.

I don't think I've done any extensive work with it since WFW 3.11 was king.

I'm sure there are applications in the wild that depend on the core ODBC calls for mission critical reasons, but they are very rare. I don't recommend you spend a lot of time learning it unless someone is paying you to do it.
 
Share this answer
 
Comments
Volynsky Alex 8-Aug-12 17:15pm    
+5
I do appreciate your feedback and understand I am missing a lot :) I think it's better to get advice early then to late and have a lot of rewriting :) thanks for the advice and your time
 
Share this answer
 
Comments
André Kraak 3-Aug-12 14:27pm    
If you have a question about or comment on a given solution use the "Have a Question or Comment?" option beneath the solution. When using this option the person who gave the solution gets an e-mail message and knows you placed a comment and can respond if he/she wants.

Please move the content of this solution to the solution you are commenting on and remove the solution.
Thank you.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900