diff options
Diffstat (limited to 'utils/sqlwrapper/sqlwrapper.cpp')
| -rw-r--r-- | utils/sqlwrapper/sqlwrapper.cpp | 453 |
1 files changed, 453 insertions, 0 deletions
diff --git a/utils/sqlwrapper/sqlwrapper.cpp b/utils/sqlwrapper/sqlwrapper.cpp new file mode 100644 index 0000000..477a066 --- /dev/null +++ b/utils/sqlwrapper/sqlwrapper.cpp @@ -0,0 +1,453 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//============================================================================= +//#include "misc.h" +//#include "stdafx.h" + +#include <windows.h> + +////// MySQL API includes +#include <WinSock.H> +#include "mysql.h" +#include "errmsg.h" + + +#include "platform.h" +#include "isqlwrapper.h" +#include "sqlhelpers.h" +#include "interface.h" +#include "utllinkedlist.h" +#include "utlvector.h" + +#include "tier0/memdbgon.h" + + +/////// + +//----------------------------------------------------------------------------- +// Purpose: Main dll entry point +// Input: hModule - our module handle +// dwReason - reason we were called +// lpReserved - bad idea that some Windows developer had some day that +// we're stuck with +//----------------------------------------------------------------------------- +#ifdef _WIN32 +BOOL APIENTRY DllMain( HANDLE hModule, + DWORD dwReason, + LPVOID lpReserved + ) +{ + switch ( dwReason ) + { + case DLL_PROCESS_ATTACH: + break; + } + + return TRUE; +} +#elif _LINUX +void __attribute__ ((constructor)) app_init(void); +void app_init(void) +{ +} +#endif + + +class CSQLWrapper : public ISQLWrapper, public ISQLHelper +{ +public: + CSQLWrapper(); + ~CSQLWrapper (); + + // ISQLWrapper + virtual void Init( const char *pchDB, const char *pchHost, const char *pchUsername, const char *pchPassword ); + virtual bool BInsert( const char *pchQueryString ); + virtual const ISQLTableSet *PSQLTableSetDescription(); + virtual IResultSet *PResultSetQuery( const char *pchQueryString ); + virtual void FreeResult(); + + // ISQLHelper + virtual bool BInternalQuery( const char *pchQueryString, MYSQL_RES **ppMySQLRes, bool bRecurse /* = true */ ); + +#ifdef DBGFLAG_VALIDATE + void Validate( CValidator &validator, char *pchName ); // Validate our internal structures +#endif + +private: + bool BConnect(); + void Disconnect(); + bool _Query( const char *pchQueryString, MYSQL_RES **result ); + + char *m_pchDB; + char *m_pchHost; + char *m_pchUsername; + char *m_pchPassword; + bool m_bConnected; + + MYSQL m_MySQL; + CSQLTableSet m_SQLTableSet; + CResultSet m_ResultSet; + bool m_bInQuery; +}; + + +class CSQLWrapperFactory : public ISQLWrapperFactory +{ +public: + CSQLWrapperFactory() {}; + ~CSQLWrapperFactory() {}; + + virtual ISQLWrapper *Create( const char *pchDB, const char *pchHost, const char *pchUsername, const char *pchPassword ); + virtual void Free( ISQLWrapper *pWrapper ); + +#ifdef DBGFLAG_VALIDATE + void Validate( CValidator &validator, char *pchName ); // Validate our internal structures +#endif + +private: + CUtlFixedLinkedList<CSQLWrapper> m_ListSQLWrapper; // use a fixed in memory data struct so we can return pointers to the interfaces +}; + +CSQLWrapperFactory g_SQLWrapperFactory; +EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CSQLWrapperFactory, ISQLWrapperFactory, INTERFACEVERSION_ISQLWRAPPER, g_SQLWrapperFactory ); + +#if 0 +//----------------------------------------------------------------------------- +// Purpose: Ensure that all of our internal structures are consistent, and +// account for all memory that we've allocated. +// Input: validator - Our global validator object +//----------------------------------------------------------------------------- +class CDLLValidate : public IValidate +{ +public: + virtual void Validate( CValidator & validator ) + { +#ifdef DBGFLAG_VALIDATE + g_SQLWrapperFactory.Validate( validator, "g_SQLWrapperFactory" ); +#endif + } +}; +CDLLValidate g_DLLValidate; +EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CDLLValidate, IValidate, INTERFACEVERSION_IVALIDATE, g_DLLValidate ); +#endif + +//----------------------------------------------------------------------------- +// Purpose: Create a SQLWrapper interface to use +// Input: pchDB - database name to connect to +// pchHost - host to connect to +// pchUsername - username to connect as +// pchPassword - password to use +// Output: a pointer to a sql wrapper interface +//----------------------------------------------------------------------------- +ISQLWrapper *CSQLWrapperFactory::Create( const char *pchDB, const char *pchHost, const char *pchUsername, const char *pchPassword ) +{ + int iSQLWrapper = m_ListSQLWrapper.AddToTail(); + CSQLWrapper &sqlWrapper = m_ListSQLWrapper[iSQLWrapper]; + sqlWrapper.Init( pchDB, pchHost, pchUsername, pchPassword ); + return &sqlWrapper; +} + + +//----------------------------------------------------------------------------- +// Purpose: Free a previously allocated sql interface +// Input: pWrapper - interface that was alloced +//----------------------------------------------------------------------------- +void CSQLWrapperFactory::Free( ISQLWrapper *pSQLWrapper ) +{ + FOR_EACH_LL( m_ListSQLWrapper, iSQLWrapper ) + { + if ( &m_ListSQLWrapper[iSQLWrapper] == ((CSQLWrapper *)pSQLWrapper) ) + { + m_ListSQLWrapper.Remove(iSQLWrapper); + break; + } + } + + Assert( iSQLWrapper != m_ListSQLWrapper.InvalidIndex() ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Ensure that all of our internal structures are consistent, and +// account for all memory that we've allocated. +// Input: validator - Our global validator object +// pchName - Our name (typically a member var in our container) +//----------------------------------------------------------------------------- +#ifdef DBGFLAG_VALIDATE +void CSQLWrapperFactory::Validate( CValidator &validator, char *pchName ) +{ + validator.Push( "CSQLWrapperFactory", this, pchName ); + + m_ListSQLWrapper.Validate( validator, "m_ListSQLWrapper" ); + + FOR_EACH_LL( m_ListSQLWrapper, iListSQLWrapper ) + { + m_ListSQLWrapper[ iListSQLWrapper ].Validate( validator, "m_ListSQLWrapper[ iListSQLWrapper ]" ); + } + + validator.Pop(); +} +#endif + +#define SAFE_DELETE( pointer ) if ( pointer != NULL ) { delete pointer; } + +//----------------------------------------------------------------------------- +// Purpose: Constructor. +//----------------------------------------------------------------------------- +CSQLWrapper::CSQLWrapper() +{ + m_pchDB = NULL; + m_pchHost = NULL; + m_pchUsername = NULL; + m_pchPassword = NULL; + m_bConnected = false; + m_bInQuery = false; + + mysql_init( &m_MySQL ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Destructor. +//----------------------------------------------------------------------------- +CSQLWrapper::~CSQLWrapper() +{ + FreeResult(); + mysql_close( &m_MySQL ); + m_bConnected = false; + + SAFE_DELETE(m_pchDB); + SAFE_DELETE(m_pchHost); + SAFE_DELETE(m_pchUsername); + SAFE_DELETE(m_pchPassword); +} + +// helper macro to alloc and copy a string to a member var +// BUGBUG Alfred: Make this a Q_function +#define COPY_STRING( dst, src ) \ + dst = new char[Q_strlen(src) + 1]; \ + Assert( dst ); \ + Q_strncpy( dst, src, Q_strlen(src) + 1); \ + dst[ Q_strlen(src) ] = 0; + + +//----------------------------------------------------------------------------- +// Purpose: Initializer. Sets up connection params but DOESN'T actually do the connection +// Input: pchDB - database name to connect to +// pchHost - host to connect to +// pchUsername - username to connect as +// pchPassword - password to use +//----------------------------------------------------------------------------- +void CSQLWrapper::Init( const char *pchDB, const char *pchHost, const char *pchUsername, const char *pchPassword ) +{ + COPY_STRING( m_pchDB, pchDB ); + COPY_STRING( m_pchHost, pchHost ); + COPY_STRING( m_pchUsername, pchUsername ); + COPY_STRING( m_pchPassword, pchPassword ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Connects to a MySQL server. +// Output: true if connect works, false otherwise +//----------------------------------------------------------------------------- +bool CSQLWrapper::BConnect() +{ + m_bInQuery = false; + + MYSQL *pMYSQL = mysql_real_connect( &m_MySQL, m_pchHost, m_pchUsername, m_pchPassword, m_pchDB, 0, NULL, 0); + if ( !pMYSQL || pMYSQL != &m_MySQL ) // on success we get our SQL pointer back + { + DevMsg( "Failed to connect to DB server (%s)\n", mysql_error(&m_MySQL) ); + return false; + } + m_bConnected = true; + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Disconnects from a mysql if you are already connected +//----------------------------------------------------------------------------- +void CSQLWrapper::Disconnect() +{ + mysql_close( &m_MySQL ); + mysql_init( &m_MySQL ); + m_bConnected = false; +} + + +//----------------------------------------------------------------------------- +// Purpose: run a query on the db with retries +// Input: pchQueryString - query string to run +// ppMySQLRes - mysql result set to set +// bRecurse - if true allow this function to call itself again (to rety the query) +// Output: true if query succeeds, false otherwise +//----------------------------------------------------------------------------- +bool CSQLWrapper::BInternalQuery( const char *pchQueryString, MYSQL_RES **ppMySQLRes, bool bRecurse ) +{ + *ppMySQLRes = NULL; + if ( !m_pchDB || !m_pchHost || !m_pchUsername || !m_pchPassword || !ppMySQLRes ) + { + return false; + } + + if ( !m_bConnected ) + { + BConnect(); // need to reconnect + } + + bool bRet = _Query( pchQueryString, ppMySQLRes ); + if ( !bRet && !m_bConnected && bRecurse ) // hmmm, got hung up when running the query + { + bRet = BInternalQuery( pchQueryString, ppMySQLRes, false ); // run the query again now we reconnected + } + + return bRet; +} + + +//----------------------------------------------------------------------------- +// Purpose: Actually runs a query on the server +// Input: pchQueryString - query string to run +// result - mysql result set to set +// Output: true if query succeeds, false otherwise, result it set on success +//----------------------------------------------------------------------------- +bool CSQLWrapper::_Query( const char *pchQueryString, MYSQL_RES **ppMySQLRes ) +{ + *ppMySQLRes = NULL; + int iResult = mysql_real_query( &m_MySQL, pchQueryString, Q_strlen(pchQueryString) ); + if ( iResult != 0 ) + { + int iErrNo = mysql_errno(&m_MySQL); + if ( iErrNo == CR_COMMANDS_OUT_OF_SYNC ) // I hate this return code, you just need to hang up and try again + { + Disconnect(); + return false; + } + else if ( iErrNo == CR_SERVER_LOST || iErrNo == CR_SERVER_GONE_ERROR ) + { + m_bConnected = false; + return false; + } + else if ( iErrNo != 0 ) + { + DevMsg( "CSQLWrapper::_Query Generic SQL query error: %s\n", mysql_error( &m_MySQL ) ); + return false; + } + } + + if ( mysql_field_count( &m_MySQL ) > 0 ) // if there are results clear them from the connection + { + *ppMySQLRes = mysql_store_result( &m_MySQL ); + } + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Runs a insert style query on the db (i.e no return set), opens the connection if it isn't currently +// Input: pchQueryString - query string to run +// Output: true if query succeeds, false otherwise +//----------------------------------------------------------------------------- +bool CSQLWrapper::BInsert( const char *pchQueryString ) +{ + if ( m_bInQuery ) + { + Assert( !m_bInQuery ); + return false; + } + + m_bInQuery = true; + MYSQL_RES *pMySQLRes = NULL; + bool bRet = BInternalQuery( pchQueryString, &pMySQLRes, true ); + if ( bRet && pMySQLRes ) + { + mysql_free_result( pMySQLRes ); + } + m_bInQuery = false; + return bRet; +} + + +//----------------------------------------------------------------------------- +// Purpose: returns a pointer to the table descriptions for this DB +// Output: table description pointer +//----------------------------------------------------------------------------- +const ISQLTableSet *CSQLWrapper::PSQLTableSetDescription() +{ + if ( m_bInQuery ) + { + Assert( !m_bInQuery ); + return NULL; + } + + m_bInQuery = true; + if ( !m_SQLTableSet.Init( this ) ) + { + return NULL; + } + + m_bInQuery = false; + return &m_SQLTableSet; +} + + +//----------------------------------------------------------------------------- +// Purpose: Runs a select style query on the db (i.e a return set), opens the connection if it isn't currently +// Input: pchQueryString - query string to run +// Output: returns a pointer to the result set (NULL on failure) +//----------------------------------------------------------------------------- +IResultSet *CSQLWrapper::PResultSetQuery( const char *pchQueryString ) +{ + if ( m_bInQuery ) + { + Assert( !m_bInQuery ); + return NULL; + } + + bool bRet = m_ResultSet.Query( pchQueryString, this ); + if ( !bRet ) + { + return NULL; + } + m_bInQuery = true; + return &m_ResultSet; +} + + +//----------------------------------------------------------------------------- +// Purpose: Free's any currently running result set +//----------------------------------------------------------------------------- +void CSQLWrapper::FreeResult() +{ + m_bInQuery = false; + m_ResultSet.FreeResult(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Ensure that all of our internal structures are consistent, and +// account for all memory that we've allocated. +// Input: validator - Our global validator object +// pchName - Our name (typically a member var in our container) +//----------------------------------------------------------------------------- +#ifdef DBGFLAG_VALIDATE +void CSQLWrapper::Validate( CValidator &validator, char *pchName ) +{ + validator.Push( "CSQLWrapper", this, pchName ); + + validator.ClaimMemory( m_pchDB ); + validator.ClaimMemory( m_pchHost ); + validator.ClaimMemory( m_pchUsername ); + validator.ClaimMemory( m_pchPassword ); + + m_SQLTableSet.Validate( validator, "m_SQLTableSet" ); + m_ResultSet.Validate( validator, "m_ResultSet" ); + + validator.Pop(); +} +#endif |