Posted: 10th Jan 2003 20:56
As I'm feeling unusally generous, I've put the source code for my SQL plug-in here :

+ Code Snippet
// sql.cpp : Defines the initialization routines for the DLL.
//
/*
This source file is (C) Nicholas Kingsley 2002-2003.
All rights reserved.  Whilst the source code is FREEWARE, I retain the copyright on the code.
Please inform me of any bug-fixes, speed improvements etc
Send any comments to : nickk@nildram.co.uk
*/

#define SQL_EXPORTS
#include "stdafx.h"
#include "sql.h"
#include "afxdb.h"
#include "sql.hpp"
#include "globstruct.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

CDatabase	database;
CRecordset	*recSet=NULL;
CDBVariant	varRecordToReturnTo;
GlobStruct* g_pGlob = NULL;

typedef struct __ERROR {
	int		flags;
	DWORD	mem;
	DWORD	size;
	} __ERROR;

struct __ERROR	error;

#define ISVALID(x) ((x)==NULL ? false : true)
#define IsEmpty(x) ((x)==NULL || ((x)->IsEOF() && (x)->IsBOF()) ? true : false)

/////////////////////////////////////////////////////////////////////////////
// CSqlApp

BEGIN_MESSAGE_MAP(CSqlApp, CWinApp)
	//{{AFX_MSG_MAP(CSqlApp)
		// NOTE - the ClassWizard will add and remove mapping macros here.
		//    DO NOT EDIT what you see in these blocks of generated code!
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CSqlApp construction

CSqlApp::CSqlApp()
{
	// TODO: add construction code here,
	// Place all significant initialization in InitInstance
	error.flags=ERROR_TOSCREEN;
	error.mem=0;
	error.size=0;		
}

/////////////////////////////////////////////////////////////////////////////
// The one and only CSqlApp object

CSqlApp theApp;


void Constructor(void)
{
	// Create memory here
}

void Destructor(void)
{
	// Free memory here
}

void ReceiveCoreDataPtr(LPVOID pCore)
{
	// Get Core Data Pointer here
	g_pGlob = (GlobStruct*)pCore;
}

void __processErrorMessage(int errNum,const char *errText,char *where)
{
	if (error.flags & ERROR_TOMEM)
	{
	LPSTR ptr;
	int size;

		size=sizeof(errNum);
		ptr=(LPSTR) error.mem;

		// Put in the error number
		ZeroMemory(ptr,error.size);
		memcpy(ptr,(char *) &errNum,size);
		memcpy((LPSTR) (ptr+size),errText,error.size-1);
	}

	if (error.flags & ERROR_TOSCREEN)
	{
		MessageBox(NULL,errText,where,MB_OK | MB_ICONWARNING);
	}
}

void SQL_SetError(int flags,DWORD mem,DWORD size)
{
	error.flags=flags;
	error.mem=mem;
	error.size=size;
}

DWORD SQL_Connect(LPSTR connectString,int options)
{
	TRY
	{
	int flags;

		flags=0;
		flags|=(options & OPEN_OPENEXCLUSIVE ? CDatabase::openExclusive : 0);
		flags|=(options & OPEN_READONLY ? CDatabase::openReadOnly : 0);
		flags|=(options & OPEN_CURSORLIB ? CDatabase::useCursorLib : 0);
		flags|=(options & OPEN_NODIALOG ? CDatabase::noOdbcDialog : 0);
		flags|=(options & OPEN_DIALOG ? CDatabase::forceOdbcDialog : 0);												

		if (database.OpenEx(_T(connectString),flags))
		{
			return ((DWORD) true);
		}
	}

	CATCH(CDBException, e)
	{
		// The error code is in e->m_nRetCode
		__processErrorMessage(e->m_nRetCode,(LPCTSTR) e->m_strError,"SQL_Connect");
	}

	END_CATCH

	return ((DWORD) false);
}

DWORD SQL_Disconnect(void)
{
	if (ISVALID(recSet))
	{
		delete recSet;
	}

	database.Close();
	return ((DWORD) true);
}

DWORD SQL_ExecuteSQL(LPSTR sql)
{
register bool cont;

	if ((error.flags & ERROR_NOERRORCHK)==0)
	{
		cont=(SQL_GetStatus(STATUS_ISOPEN,0) && sql ? true : false);
	}
	else
	{
		cont=(sql ? true : false);
	}

	if (cont)
	{
		TRY 
		{
			database.ExecuteSQL(_T(sql));
		} 

		
		CATCH(CDBException, e)
		{
			// The error code is in e->m_nRetCode
			__processErrorMessage(e->m_nRetCode,(LPCTSTR) e->m_strError,"SQL_ExecuteSQL");
			return ((DWORD) false);
		}

		END_CATCH		
	}

	return ((DWORD) false);
}

DWORD SQL_ConnectTable(LPSTR tableName,int openType,int options)
{
	recSet=new CRecordset(&database); 
	TRY
	{
		if (ISVALID(recSet))
		{
		register unsigned int flags1,flags2;

			flags1=AFX_DB_USE_DEFAULT_TYPE;
			flags1|=(openType & OPEN_DYNASET ? CRecordset::dynaset : 0);
			flags1|=(openType & OPEN_SNAPSHOT ? CRecordset::snapshot : 0);
			flags1|=(openType & OPEN_DYNAMIC	? CRecordset::dynamic : 0);
			flags1|=(openType & OPEN_FORWARDONLY	? CRecordset::forwardOnly : 0);

			flags2=CRecordset::none;
			flags2|=(options & OPEN2_APPENDONLY	? CRecordset::appendOnly : 0);
			flags2|=(options & OPEN2_READONLY ?	CRecordset::readOnly : 0);
			flags2|=(options & OPEN2_BULKADD ? CRecordset::optimizeBulkAdd : 0);		
			flags2|=(options & OPEN2_MULTROWFETCH ? CRecordset::useMultiRowFetch : 0);
			flags2|=(options & OPEN2_SKIPDEL ? CRecordset::skipDeletedRecords : 0);
			flags2|=(options & OPEN2_USEBOOKMARK ? CRecordset::useBookmarks : 0);
			flags2|=(options & OPEN2_NODIRTY ? CRecordset::noDirtyFieldCheck : 0);
			flags2|=(options & OPEN2_EXECUTEDIR	? CRecordset::executeDirect : 0);
			flags2|=(options & OPEN2_EXTENDEDFETCH ? CRecordset::useExtendedFetch : 0);
			flags2|=(options & OPEN2_ALLOCMULTI	? CRecordset::userAllocMultiRowBuffers : 0);
			
			if (recSet->Open(flags1,_T(tableName),flags2)==0)
			{
				return ((DWORD) false);
			}
		}
		else
		{
			return ((DWORD) false);
		}
	}

	CATCH(CDBException, e)
	{
		// The error code is in e->m_nRetCode
		__processErrorMessage(e->m_nRetCode,(LPCTSTR) e->m_strError,"SQL_ConnectTable");
		return ((DWORD) false);
	}

	END_CATCH
	return ((DWORD) true);
}

DWORD SQL_IsBOF(void)
{
	return ((DWORD) (ISVALID(recSet) && SQL_GetStatus(STATUS_ISOPEN,0) ? recSet->IsBOF() : true));
}

DWORD SQL_IsEOF(void)
{
	return ((DWORD) (ISVALID(recSet) && SQL_GetStatus(STATUS_ISOPEN,0) ? recSet->IsEOF() : true));
}

DWORD SQL_IsDeleted(void)
{
	return ((DWORD) (ISVALID(recSet) && SQL_GetStatus(STATUS_ISOPEN,0) ? recSet->IsDeleted() : true));
}

DWORD SQL_MoveFirst(void)
{
	if (ISVALID(recSet) && SQL_GetStatus(STATUS_ISOPEN,0))
	{
		TRY 
		{
			if (IsEmpty(recSet))
			{
				// Both end of file, so dont do anything
				return ((DWORD) false);
			}

			recSet->MoveFirst();
		}

		CATCH(CDBException, e)
		{
			// The error code is in e->m_nRetCode
			__processErrorMessage(e->m_nRetCode,(LPCTSTR) e->m_strError,"SQL_MoveFirst");
			return ((DWORD) false);
		}

		END_CATCH
		return ((DWORD) true);
	}

	return ((DWORD) false);
}

DWORD SQL_MoveLast(void)
{
	if (ISVALID(recSet) && SQL_GetStatus(STATUS_ISOPEN,0))
	{
		if (IsEmpty(recSet))
		{
			// Both end of file, so dont do anything
			return ((DWORD) false);
		}

		TRY 
		{
			recSet->MoveLast();
		}

		CATCH(CDBException, e)
		{
			// The error code is in e->m_nRetCode
			__processErrorMessage(e->m_nRetCode,(LPCTSTR) e->m_strError,"SQL_MoveLast");
			return ((DWORD) false);
		}

		END_CATCH
		return ((DWORD) true);
	}

	return ((DWORD) false);
}

unsigned __int64	SQL_FieldCount(void)
{
	return ((unsigned __int64) (!ISVALID(recSet) ? 0 : recSet->GetODBCFieldCount()));
}

unsigned __int64	SQL_RecordCount(void)
{
unsigned __int64 count;

	count=0;
	if (ISVALID(recSet) && SQL_GetStatus(STATUS_ISOPEN,0))
	{
		TRY 
		{
			if (SQL_MoveFirst())
			{
				while (!SQL_IsEOF())
				{
					if (!SQL_IsDeleted())
					{
						count++;						
					}

					SQL_MoveNext();
				}
			}
		}

		CATCH(CDBException, e)
		{
			// The error code is in e->m_nRetCode
			__processErrorMessage(e->m_nRetCode,(LPCTSTR) e->m_strError,"SQL_RecordCount");
			return ((unsigned __int64) 0);
		}

		END_CATCH
	}

	return ((unsigned __int64) count);
}

void SQL_GetFieldValue(LPSTR name,DWORD ptr)
{
CString strValue;
LPSTR	addr;

	if (ISVALID(recSet) && SQL_GetStatus(STATUS_ISOPEN,0)) 
	{
		if ((addr=(LPSTR) ptr)!=NULL)
		{
			if (!IsEmpty(recSet))
			{
				TRY
				{
					recSet->GetFieldValue(_T(name),strValue);
				}

				CATCH(CDBException, e)
				{
					// The error code is in e->m_nRetCode
					__processErrorMessage(e->m_nRetCode,(LPCTSTR) e->m_strError,"SQL_GetFieldValue");
					return;
				}

				END_CATCH
				strcpy(addr,strValue);
			}
			else
			{			
			}
		}
	}
}

DWORD SQL_MoveNext(void)
{
	if (ISVALID(recSet))
	{			
		if (!IsEmpty(recSet) && SQL_GetStatus(STATUS_ISOPEN,0)) 
		{
			TRY 
			{
				recSet->MoveNext();
			}

			CATCH(CDBException, e)
			{
				// The error code is in e->m_nRetCode
				__processErrorMessage(e->m_nRetCode,(LPCTSTR) e->m_strError,"SQL_MoveNext");
				return ((DWORD) false);
			}

			CATCH(CMemoryException, m)
			{
				__processErrorMessage(-1,"Error","SQL_MoveNext");
				return ((DWORD) false);

			}

			END_CATCH
			return ((DWORD) true);
		}
	}

	return ((DWORD) false);
}

DWORD SQL_MovePrev(void)
{
	if (ISVALID(recSet))
	{
		if (!IsEmpty(recSet) && SQL_GetStatus(STATUS_ISOPEN,0))
		{
			TRY 
			{
				recSet->MovePrev();
			}

			CATCH(CDBException, e)
			{
				// The error code is in e->m_nRetCode
				__processErrorMessage(e->m_nRetCode,(LPCTSTR) e->m_strError,"SQL_MovePrev");
				return ((DWORD) false);
			}

			END_CATCH
			return ((DWORD) true);
		}
	}

	return ((DWORD) false);
}

DWORD SQL_Delete(void)
{
	if (ISVALID(recSet) && SQL_GetStatus(STATUS_ISOPEN,0))
	{		
		if (IsEmpty(recSet) || SQL_IsDeleted() || !recSet->CanUpdate())
		{
			// Both end of file, so dont do anything
			return ((DWORD) false);
		}
		
		TRY 
		{
			recSet->Delete();
			recSet->Requery();
		}

		CATCH(CDBException, e)
		{
			// The error code is in e->m_nRetCode
			__processErrorMessage(e->m_nRetCode,(LPCTSTR) e->m_strError,"SQL_Delete");
			return ((DWORD) false);
		}

		END_CATCH
		return ((DWORD) true);
	}

	return ((DWORD) false);
}

DWORD SQL_GetConnect(DWORD pOldString)
{
register LPSTR pReturnString=NULL;
CString temp;
register int len;

	// Delete old string
	if(pOldString) g_pGlob->CreateDeleteString ( (DWORD*)&pOldString, 0 );
	
	// Return string pointer
	temp=database.GetConnect();
	len=temp.GetLength()+1;
	g_pGlob->CreateDeleteString ((DWORD*) &pReturnString, len);
	ZeroMemory(pReturnString,len);
	memcpy(pReturnString,temp,len-1);
	return ((DWORD) pReturnString);
}

DWORD SQL_GetDatabaseName(DWORD pOldString)
{
register LPSTR pReturnString=NULL;
CString temp;
register int len;

	// Delete old string
	if(pOldString) g_pGlob->CreateDeleteString ( (DWORD*)&pOldString, 0 );
	
	// Return string pointer
	temp=database.GetDatabaseName();
	len=temp.GetLength()+1;
	g_pGlob->CreateDeleteString ((DWORD*) &pReturnString, len);
	ZeroMemory(pReturnString,len);
	memcpy(pReturnString,temp,len-1);
	return ((DWORD) pReturnString);
}

DWORD SQL_GetTableName(DWORD pOldString)
{
register LPSTR pReturnString=NULL;
CString temp;
register int len;

	// Delete old string
	if(pOldString) g_pGlob->CreateDeleteString ( (DWORD*)&pOldString, 0 );
	
	if (ISVALID(recSet))
	{
	// Return string pointer
		temp=recSet->GetTableName();
		len=temp.GetLength()+1;
	}
	else
	{
		len=0;
	}

	g_pGlob->CreateDeleteString ((DWORD*) &pReturnString, len);
	if (len)
	{
		ZeroMemory(pReturnString,len);
		memcpy(pReturnString,temp,len-1);
	}
	return ((DWORD) pReturnString);
}

DWORD SQL_GetStatus(int which,DWORD ptr)
{
void *pv;

	pv=(void *) ptr;
	switch (which) {
		case	STATUS_ISOPEN	:	// Is database open ?
									return ((DWORD) database.IsOpen() ? true : false);
									break;
		case	STATUS_TRANSACT	:	// Can transact ?
									return ((DWORD) database.CanTransact() ? true : false);
									break;
		case	STATUS_UPDATE	:	// Can update ?
									return ((DWORD) database.CanUpdate() ? true : false);
									break;
		case	STATUS_APPEND	:	// Can append ?
									return ((DWORD) ISVALID(recSet) ? recSet->CanAppend() : false);
									break;
		case	STATUS_BOOKMARK	:	// Can bookmark ?
									return ((DWORD) ISVALID(recSet) ? recSet->CanBookmark() : false);
									break;
		case	STATUS_RESTART	:	// Can restart ?
									return ((DWORD) ISVALID(recSet) ? recSet->CanRestart() : false);
									break;
		case	STATUS_SCROLL	:	// Can scroll ?
									return ((DWORD) ISVALID(recSet) ? recSet->CanScroll() : false);
									break;
		case	STATUS_TRANSACT2:	// Can record transact ?
									return ((DWORD) ISVALID(recSet) ? recSet->CanTransact() : false);
									break;
		case	STATUS_UPDATE2	:	// Can record update ?
									return ((DWORD) ISVALID(recSet) ? recSet->CanUpdate() : false);
									break;
		case	STATUS_DIRTY	:	// Is the field dirty ?
		case	STATUS_NULL		:	// Is the field NULL
		case	STATUS_NULLABLE	:	// Is the field NULLable ?
									if (ISVALID(recSet))
									{
										TRY 
										{
											switch (which) {
												case	STATUS_DIRTY	: 	return (recSet->IsFieldDirty(pv));
																			break;
												case	STATUS_NULL		: 	return (recSet->IsFieldNull(pv));
																			break;
												case	STATUS_NULLABLE : 	return (recSet->IsFieldNullable(pv));
																			break;
												};																		
										}

										CATCH(CDBException, e)
										{
											// The error code is in e->m_nRetCode
											__processErrorMessage(e->m_nRetCode,(LPCTSTR) e->m_strError,"SQL_Status");
										}

										END_CATCH
									}
									break;
		};

	return (STATUS_NOTKNOWN);
}

void SQL_GetSetBookmark(bool set)
{
	if (ISVALID(recSet) && recSet->CanBookmark())
	{
		if (set)
		{
			// Set a bokmark
			recSet->GetBookmark(varRecordToReturnTo);
		}
		else
		{
			recSet->SetBookmark(varRecordToReturnTo);
		}
	}
}

void SQL_SetAbsPosition(long rows)
{
	if (ISVALID(recSet))
	{
		TRY 
		{
			recSet->SetAbsolutePosition(rows);
		}

		CATCH(CDBException, e)
		{
			// The error code is in e->m_nRetCode
			__processErrorMessage(e->m_nRetCode,(LPCTSTR) e->m_strError,"SQL_SetAbsPosition");
		}

		END_CATCH

	}
}

long SQL_GetCurrentRecordPos(void)
{
 CRecordsetStatus rStatus;

	if (ISVALID(recSet))
	{
		TRY 
		{
			recSet->GetStatus(rStatus);
		}

		CATCH(CDBException, e)
		{
			// The error code is in e->m_nRetCode
			__processErrorMessage(e->m_nRetCode,(LPCTSTR) e->m_strError,"SQL_CurrentRecordPos");
			return (AFX_CURRENT_RECORD_UNDEFINED);
		}

		END_CATCH

		return (rStatus.m_lCurrentRecord);
	}

	return (AFX_CURRENT_RECORD_UNDEFINED);
}
Posted: 26th Jan 2003 15:28
Great! But how do I use it?
Posted: 26th Jan 2003 16:54
You compile it with Visual C++. Or alternatively, download it from MrTAToads site.
Posted: 29th Jan 2003 22:42
That way is certainly the easiest.