summaryrefslogtreecommitdiff
path: root/utils/sqlwrapper/sqlwrapper.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /utils/sqlwrapper/sqlwrapper.cpp
downloadarchived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.tar.xz
archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.zip
Diffstat (limited to 'utils/sqlwrapper/sqlwrapper.cpp')
-rw-r--r--utils/sqlwrapper/sqlwrapper.cpp453
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