ODBC Objects for C++

NOW WITH UNICODE SUPPORT! This is a useful library for the Windows API programmer. This library wraps-up the complex functionality of the ODBC API in to easy to understand and use interfaces. This source code just demonstrates the funcionality of the ODBC Objects library. It does not check all functions for successful (TRUE) return values as it would in production code. This application uses 32-bit ODBC. If using 64-bit Windows, access the 32-bit ODBC drivers from C:\Windows\SysWOW64\odbcad32.exe. Download the library and this source code: ODBCOBJ.zip

#include <windows.h>
#include "_odbcobj.h"
#include <iostream>
#include <string>
using namespace std;

struct STACK{
	char szname[512];
	STACK *next;
};

int main(int argc, char *argv[])
{
	HINSTANCE hDll;
	IObjectFactory *fact;
	IdbEnvironment *env;
	IdbDriverCatalog *dct;
	IdbDataSourceCatalog *dsc;
	IdbConnection *con;
	IdbResultsetEx *rsex;
	IdbResultset *rs;
	IdbColumnCatalog *cols;
	IdbTableCatalog *tbls;
	char *szSQL = nullptr;
	string sztmp;
	int i;
	STACK *next, *stack = 0;

	hDll = LoadLibraryA("OdbcObj.dll");
	if(hDll == nullptr)
	{
	MessageBoxA(nullptr, "Could not load ODBCOBJ.dll", "ERROR", 0);
	return 0;
	}
	i = GetLastError();
	i = CreateObjectFactory(hDll, &fact);
	fact->CreateObject(I_ENVIRONMENT_V1, (void **)&env);
	fact->CreateObject(I_DRIVER_CATALOG_V1, (void **)&dct);
	fact->CreateObject(I_DATASOURCE_CATALOG_V1, (void **)&dsc);
	fact->CreateObject(I_CONNECTION_V1, (void **)&con);
	fact->CreateObject(I_RESULTSET_EX_V1, (void **)&rsex);
	fact->CreateObject(I_RESULTSET_V1, (void **)&rs);
	fact->CreateObject(I_COLUMN_CATALOG_V1, (void **)&cols);
	fact->CreateObject(I_TABLE_CATALOG_V1, (void **)&tbls);
	fact->DelRef();
	i = env->Open();

	// OPEN DRIVER CATALOG
	i = dct->Open(env);
	for(i = 0; i < dct->DriverCount(); i++)
	{
	if(next = new STACK)
	{
		IdbDriver *drv = dct->DriverByIndex(i);
		strcpy(next->szname, "DRIVER=");
		strcat(next->szname, drv->Name());
		next->next = stack;
		stack = next;
	}
	}

	// OPEN DATASOURCE CATALOG
	i = dsc->Open(env);
	for(i = 0; i < dsc->DataSourceCount(); i++)
	{
	if(next = new STACK)
	{
		IdbDataSource *dsn = dsc->DataSourceByIndex(i);
		strcpy(next->szname, "DSN=");
		strcat(next->szname, dsn->Name());
		next->next = stack;
		stack = next;
	}
	}
	next = stack;
	for(i = 1; next; i++)
	{
	cout << i << ". " << next->szname << endl;
	next = next->next;
	}
	int idx;
	cout << "Choose the datasource you want to use: ";
	cin >> idx;
	cout << endl;
	if(idx < 1 || idx >= i)
	goto clean_up;
	next = stack;
	for(i = 1; next; i++)
	{
	if(idx == i)
		break;
	next = next->next;
	}
	// CONNECT TO THE DATABASE USING DRIVER/DSN WITH DRIVER PROMPT = TRUE
	// SO THAT IT WILL PROMPT THE USER FOR ADDITIONAL INFO
	i = con->Open(next->szname, env, 10, FALSE, TRUE);
	if(i == FALSE)
	{
	for(i = 0; i < con->ErrorCount(); i++)
	{
		LONGLONG code = con->ErrorCode(i);
		MessageBoxA(0, con->ErrorDescription(i), (char *)&code, 0);
	}
	goto clean_up;
	}

	// SHOW THE NEW CONNECTION STRING
	cout << "Connection String:" << endl;
	cout << con->Name() << endl << endl;

	cout << "Hit enter to continue." << endl;
	getline(cin, sztmp);

	// NOT NEEDED ANY MORE
	while(stack)
	{
	next = stack->next;
	delete stack;
	stack = next;
	}

	// GET LIST OF TABLES IN THE DATABASE
	i = tbls->Open("table", con, 30);
	for(i = 0; i < tbls->TableCount(); i++)
	{
	if(next = new STACK)
	{
		strcpy(next->szname, tbls->TableByIndex(i)->Name() );
		strcat(next->szname, "\t");
		strcat(next->szname, tbls->TableByIndex(i)->Type());
		next->next = stack;
		stack = next;
	}
	}
	tbls->Close();

	// GET LIST OF VIEWS (QUERIES) IN THE DATABASE
	i = tbls->Open("view", con, 30);
	for(i = 0; i < tbls->TableCount(); i++)
	{
	if(next = new STACK)
	{
		strcpy(next->szname, tbls->TableByIndex(i)->Name() );
		strcat(next->szname, "\t");
		strcat(next->szname, tbls->TableByIndex(i)->Type());
		next->next = stack;
		stack = next;
	}
	}

	next = stack;
	for(i = 1; next; i++)
	{
	cout << i << ". " << next->szname << endl;
	next = next->next;
	}
	cout << "Choose the table/view you want to use: ";
	cin >> idx;
	cout << endl;
	if(idx < 1 || idx >= i)
	goto clean_up;
	next = stack;
	for(i = 1; next; i++)
	{
	if(idx == i)
		break;
	next = next->next;
	}

	for(i = 0; next->szname[i] != '\t'; i++);
	next->szname[i] = 0;

	// GET LIST OF COLUMNS IN THE TABLE/VIEW CHOSEN BY THE USER
	i = cols->Open(next->szname, con, 30);
	for(i = 0; i < cols->ColumnCount(); i++)
	cout << cols->ColumnByIndex(i)->Name() << "\t" << cols->ColumnByIndex(i)->FieldTypeName() << endl;

	cout << endl << "Hit enter to continue." << endl;
	getline(cin, sztmp);

	szSQL = new char[1024];
	if(!szSQL)
	goto clean_up;
	strcpy(szSQL,"SELECT * FROM ");
	strcat(szSQL, next->szname);

	i = rs->Open(szSQL, con, 30);
	if(i != FALSE)
	{
	while(!rs->IsEOF())
	{
		for(i = 0; i < rs->FieldCount(); i++)
		{
		CDataTypesEnum d = rs->FieldByIndex(i)->CDataType();
		FieldTypesEnum f = rs->FieldByIndex(i)->FieldType();
		int t;
		double dbl;
		short s;
		long l;
		dbDATE *dat;
		dbTIME *tim;
		dbTIMESTAMP *ts;

		// CHECK FIELD FOR nullptr VALUE
		if(rs->FieldByIndex(i)->Value())
		{
			switch(d)
			{
			case dbChar:
			cout << "[" << (char *)rs->FieldByIndex(i)->Value() << "]";
			break;
			case dbUChar:
			switch(f)
			{
			case dbFldBit:
			case dbFldTinyInt:
				t = *(unsigned char *)rs->FieldByIndex(i)->Value();
				cout << "[" << t << "]";
				break;
			case dbFldBinary:
			case dbFldVarBinary:
			case dbFldLongVarBinary:
				// not supported in console window
				break;
			}
			break;
			case dbDouble:
			dbl = *(double *)rs->FieldByIndex(i)->Value();
			cout << "[" << dbl << "]";
			break;
			case dbFloat:
			dbl = *(float *)rs->FieldByIndex(i)->Value();
			cout << "[" << dbl << "]";
			break;
			case dbShort:
			s = *(short *)rs->FieldByIndex(i)->Value();
			cout << "[" << s << "]";
			break;
			case dbLong:
			l = *(long *)rs->FieldByIndex(i)->Value();
			cout << "[" << l << "]";
			break;
			case dbDate:
			dat = (dbDATE *)rs->FieldByIndex(i)->Value();
			cout << "[" << dat->month << "/" << dat->day << "/" << dat->year << "]";
			break;
			case dbTime:
			tim = (dbTIME *)rs->FieldByIndex(i)->Value();
			cout << "[" << tim->hour << ":" << tim->minute << ":" << tim->second << "]";
			break;
			case dbTimeStamp:
			ts = (dbTIMESTAMP *)rs->FieldByIndex(i)->Value();
			cout << "[" << ts->month << "/" << ts->day << "/" << ts->year << " "
			<< ts->hour << ":" << ts->minute << ":" << ts->second << "." << ts->fraction << "]";
			break;
			}
		}
		else
		{
			cout << "NULL";
		}
		}
		cout << endl;
		rs->MoveNext();
	}
	}
	else
	{
	for(i = 0; i < rs->ErrorCount(); i++)
	{
		LONGLONG code = rs->ErrorCode(i);
		MessageBoxA(0, rs->ErrorDescription(i), (char *)&code, 0);
	}
	}

	/* // USE EXTENDED RECORDSET -- REQUIRES MORE ODBC FUNCTIONS THAN BASIC RECORDSET
	// IF NEEDING TO UPDATE A UNICODE FIELD USE con->ExecuteW() (UPDATE STATEMENT)
	i = rsex->Open(szSQL, con, 30, dbKeysetDrivenCursor, dbLockConcur, 5);
	if(i != FALSE)
	{
	while(!rs->IsEOF())
	{
		rs->MoveNext();
	}
	}
	else
	{
	for(i = 0; i < rsex->ErrorCount(); i++)
	{
		LONGLONG code = rsex->ErrorCode(i);
		MessageBoxA(0, rsex->ErrorDescription(i), (char *)&code, 0);
	}
	}//*/

clean_up:
	if(szSQL)
	delete []szSQL;
	while(stack)
	{
	next = stack->next;
	delete stack;
	stack = next;
	}
	// DELETE REFERENCE WILL ALSO CLOSE THE OBJECTS IF THERE ARE NO MORE REFERENCES TO THE OBJECT
	tbls->DelRef();
	cols->DelRef();
	rs->DelRef();
	rsex->DelRef();
	con->DelRef();
	dct->DelRef();
	dsc->DelRef();
	env->DelRef();

	return 0;
}