diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /utils/sqlwrapper | |
| download | archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.tar.xz archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.zip | |
Diffstat (limited to 'utils/sqlwrapper')
| -rw-r--r-- | utils/sqlwrapper/sqlhelpers.cpp | 543 | ||||
| -rw-r--r-- | utils/sqlwrapper/sqlhelpers.h | 162 | ||||
| -rw-r--r-- | utils/sqlwrapper/sqlwrapper.cpp | 453 | ||||
| -rw-r--r-- | utils/sqlwrapper/sqlwrapper.vcproj | 200 |
4 files changed, 1358 insertions, 0 deletions
diff --git a/utils/sqlwrapper/sqlhelpers.cpp b/utils/sqlwrapper/sqlhelpers.cpp new file mode 100644 index 0000000..878c13c --- /dev/null +++ b/utils/sqlwrapper/sqlhelpers.cpp @@ -0,0 +1,543 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//============================================================================= + +//#include "misc.h" +#include "isqlwrapper.h" +#include "time.h" +#include "sqlhelpers.h" + +#include "tier0/memdbgon.h" + +#pragma warning( disable : 4706 ) // Warning C4706: assignment within conditional expression + +//----------------------------------------------------------------------------- +// Purpose: converts between a MySQL field type and our column types +//----------------------------------------------------------------------------- +EColumnType GetEColumnTypeFromESQLFieldType( ESQLFieldType eSQLFieldType ) +{ + switch( eSQLFieldType ) + { + case FIELD_TYPE_DECIMAL: + case FIELD_TYPE_TINY: + case FIELD_TYPE_SHORT: + case FIELD_TYPE_LONG: + return SQL_INT; + break; + case FIELD_TYPE_FLOAT: + case FIELD_TYPE_DOUBLE: + return SQL_FLOAT; + break; + case FIELD_TYPE_LONGLONG: + return SQL_UINT64; + break; + case FIELD_TYPE_NULL: + case FIELD_TYPE_INT24: + return SQL_NONE; + break; + case FIELD_TYPE_DATE: + case FIELD_TYPE_TIMESTAMP: + case FIELD_TYPE_TIME: + case FIELD_TYPE_DATETIME: + case FIELD_TYPE_YEAR: + case FIELD_TYPE_NEWDATE: + return SQL_TIME; + break; + case FIELD_TYPE_ENUM: + case FIELD_TYPE_SET: + case FIELD_TYPE_TINY_BLOB: + case FIELD_TYPE_MEDIUM_BLOB: + case FIELD_TYPE_LONG_BLOB: + case FIELD_TYPE_BLOB: + return SQL_NONE; + break; + case FIELD_TYPE_VAR_STRING: + case FIELD_TYPE_STRING: + return SQL_STRING; + break; + } + return SQL_NONE; +} + + +//----------------------------------------------------------------------------- +// Purpose: Constructor. +//----------------------------------------------------------------------------- +CSQLColumn::CSQLColumn( const char *pchName, ESQLFieldType eSQLFieldType ) +{ + Assert( pchName ); + Q_strncpy( m_rgchName, pchName, sizeof( m_rgchName ) ); + m_ESQLFieldType = eSQLFieldType; + m_EColumnType = GetEColumnTypeFromESQLFieldType( m_ESQLFieldType ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CSQLTable::CSQLTable( const char *pchName ) +{ + Assert( pchName ); + Q_strncpy( m_rgchName, pchName, sizeof( m_rgchName ) ); +} + + +//----------------------------------------------------------------------------- +// Purpose: copy constructor (used by CUtlVector) +//----------------------------------------------------------------------------- +CSQLTable::CSQLTable( CSQLTable const &sqlTable ) +{ + Q_strncpy( m_rgchName, sqlTable.m_rgchName, sizeof( m_rgchName ) ); + for ( int iSQLColumn = 0; iSQLColumn < sqlTable.m_VecSQLColumn.Count(); iSQLColumn++ ) + { + m_VecSQLColumn.AddToTail( CSQLColumn( sqlTable.m_VecSQLColumn[iSQLColumn].PchColumnName(), + sqlTable.m_VecSQLColumn[iSQLColumn].GetESQLFieldType() ) ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: add a new column to this data description +//----------------------------------------------------------------------------- +void CSQLTable::AddColumn( const char *pchName, ESQLFieldType eSQLFieldType ) +{ + m_VecSQLColumn.AddToTail( CSQLColumn( pchName, eSQLFieldType ) ); +} + + +//----------------------------------------------------------------------------- +// 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 CSQLTable::Validate( CValidator &validator, char *pchName ) +{ + validator.Push( "CSQLTable", this, pchName ); + + m_VecSQLColumn.Validate( validator, "m_VecSQLColumn" ); + + validator.Pop(); +} +#endif + + +//----------------------------------------------------------------------------- +// Purpose: Create the table descriptions from a data base +//----------------------------------------------------------------------------- +bool CSQLTableSet::Init( ISQLHelper *pSQLHelper ) +{ + m_VecSQLTable.RemoveAll(); + + MYSQL_RES *pMySQLRes = NULL; + bool bRet = pSQLHelper->BInternalQuery( "show tables", &pMySQLRes ); + if ( !bRet || !pMySQLRes ) + { + return false; + } + + MYSQL_ROW row; + while ( ( row = mysql_fetch_row( pMySQLRes ) ) ) + { + CSQLTable sqlTable( row[0] ); + m_VecSQLTable.AddToTail( sqlTable ); + } + mysql_free_result( pMySQLRes ); + + for ( int i = 0; i < m_VecSQLTable.Count(); i++ ) + { + CSQLTable &sqlTable = m_VecSQLTable[i]; + + char szQuery[512]; + Q_snprintf( szQuery, sizeof(szQuery), "select * from %s limit 1", sqlTable.PchName() ); + MYSQL_RES *pMySQLRes = NULL; + bool bRet = pSQLHelper->BInternalQuery( szQuery, &pMySQLRes ); + if ( !bRet || !pMySQLRes ) + { + m_VecSQLTable.RemoveAll(); + return false; + } + + uint nFields = mysql_num_fields( pMySQLRes ); + Assert( nFields > 0 ); + MYSQL_FIELD *pFields = mysql_fetch_fields( pMySQLRes ); + for( uint i = 0; i < nFields; i++) + { + sqlTable.AddColumn( pFields[i].name, pFields[i].type ); + } + + mysql_free_result( pMySQLRes ); + } + + m_bInit = true; + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: returns a pointer to each table in the db +//----------------------------------------------------------------------------- +const ISQLTable *CSQLTableSet::PSQLTable( int iSQLTable ) const +{ + return &m_VecSQLTable[iSQLTable]; +} + + +//----------------------------------------------------------------------------- +// 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 CSQLTableSet::Validate( CValidator &validator, char *pchName ) +{ + validator.Push( "CSQLTableSet", this, pchName ); + + m_VecSQLTable.Validate( validator, "m_VecSQLTable" ); + for ( int i = 0; i < m_VecSQLTable.Count(); i++ ) + { + m_VecSQLTable[i].Validate( validator, "m_VecSQLTable[i]" ); + } + + validator.Pop(); +} +#endif + + +//----------------------------------------------------------------------------- +// Purpose: constructor +//----------------------------------------------------------------------------- +CSQLRow::CSQLRow( MYSQL_ROW *pMySQLRow, const ISQLTable *pSQLTableDescription, int cRowData ) +{ + Assert( pMySQLRow && pSQLTableDescription ); + for ( int iRowData = 0; iRowData < cRowData; iRowData++ ) + { + struct SQLRowData_s sqlRowData; + sqlRowData.eColumnType = pSQLTableDescription->GetEColumnType( iRowData ); + const CSQLTable *tableDesc = static_cast<const CSQLTable *>(pSQLTableDescription); + switch ( tableDesc->GetESQLFieldType( iRowData ) ) + { + case FIELD_TYPE_TINY: + case FIELD_TYPE_DECIMAL: + case FIELD_TYPE_SHORT: + case FIELD_TYPE_LONG: + sqlRowData.data.nData = atoi( ( *pMySQLRow ) [iRowData] ); + break; + + case FIELD_TYPE_FLOAT: + case FIELD_TYPE_DOUBLE: + sqlRowData.data.flData = atof( ( *pMySQLRow ) [iRowData] ); + break; + + case FIELD_TYPE_NULL: + case FIELD_TYPE_INT24: + memset( &sqlRowData.data, 0x0, sizeof( sqlRowData.data ) ); + break; + + case FIELD_TYPE_LONGLONG: + sqlRowData.data.nData = atoi( ( *pMySQLRow ) [iRowData] ); + break; + + case FIELD_TYPE_TIMESTAMP: + case FIELD_TYPE_DATETIME: + { + struct tm tm; + char num[5]; + char szValue[64]; + Q_memset( &tm, 0x0, sizeof( tm ) ); + Q_strncpy( szValue, ( *pMySQLRow )[iRowData], sizeof( szValue ) ); + const char *str= szValue; + num[0] =*str++; num[1] =*str++; num[2] =*str++; num[3] =*str++; num[4] = 0; + tm.tm_year = strtol( num, 0, 10 ) - 1900; + if (*str == '-') str++; + num[0] = *str++; num[1] = *str++; num[2] = 0; + tm.tm_mon = strtol( num, 0, 10 ) - 1; + if (*str == '-') str++; + num[0] = *str++; num[1] = *str++; num[2] = 0; + tm.tm_mday = strtol( num, 0, 10 ); + num[0] = *str++; num[1] = *str++; num[2] = 0; + tm.tm_hour = strtol( num, 0, 10 ); + if (*str == ':') str++; + num[0] = *str++; num[1] = *str++; num[2] = 0; + tm.tm_min = strtol( num, 0, 10 ); + if (*str == ':') str++; + num[0] = *str++; num[1] = *str++; num[2] = 0; + tm.tm_sec = strtol( num, 0, 10 ); + + sqlRowData.data.ulTime = _mktime64( &tm ); + } + break; + + case FIELD_TYPE_TIME: + case FIELD_TYPE_DATE: + case FIELD_TYPE_YEAR: + case FIELD_TYPE_NEWDATE: + sqlRowData.data.ulTime = time( NULL ); // FIXME + Assert( !"Not implemented" ); + break; + + case FIELD_TYPE_ENUM: + case FIELD_TYPE_SET: + case FIELD_TYPE_TINY_BLOB: + case FIELD_TYPE_MEDIUM_BLOB: + case FIELD_TYPE_LONG_BLOB: + case FIELD_TYPE_BLOB: + memset( &sqlRowData.data, 0x0, sizeof( sqlRowData.data ) ); + Assert( !"Not implemented" ); + break; + + case FIELD_TYPE_VAR_STRING: + case FIELD_TYPE_STRING: + { + int cchString = Q_strlen( ( char * )( *pMySQLRow )[iRowData] ); + char *pchNewString = new char[ cchString + 1 ]; + m_VecPchStoredStrings.AddToTail( pchNewString ); + Q_strncpy( pchNewString, ( char * )( *pMySQLRow )[iRowData], cchString + 1 ); + pchNewString[ cchString ] = 0; + sqlRowData.data.pchData = pchNewString; + } + break; + } + m_VecSQLRowData.AddToTail( sqlRowData ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: destructor +//----------------------------------------------------------------------------- +CSQLRow::~CSQLRow() +{ + for ( int ipch = 0; ipch < m_VecPchStoredStrings.Count(); ipch++ ) + { + delete m_VecPchStoredStrings[ipch]; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: pointer to the char data +//----------------------------------------------------------------------------- +const char *CSQLRow::PchData( int iRowData ) const +{ + Assert( m_VecSQLRowData[iRowData].eColumnType == SQL_STRING ); + return m_VecSQLRowData[iRowData].data.pchData; +} + + +//----------------------------------------------------------------------------- +// Purpose: returns the int value of this column +//----------------------------------------------------------------------------- +int CSQLRow::NData( int iRowData ) const +{ + Assert( m_VecSQLRowData[iRowData].eColumnType == SQL_INT); + return m_VecSQLRowData[iRowData].data.nData; +} + + +//----------------------------------------------------------------------------- +// Purpose: returns the uint64 value of this column +//----------------------------------------------------------------------------- +uint64 CSQLRow::UlData( int iRowData ) const +{ + Assert( m_VecSQLRowData[iRowData].eColumnType == SQL_UINT64); + return m_VecSQLRowData[iRowData].data.ulData; +} + + +//----------------------------------------------------------------------------- +// Purpose: returns the float value of this column +//----------------------------------------------------------------------------- +float CSQLRow::FlData( int iRowData ) const +{ + Assert( m_VecSQLRowData[iRowData].eColumnType == SQL_FLOAT ); + return m_VecSQLRowData[iRowData].data.flData; +} + + +//----------------------------------------------------------------------------- +// Purpose: returns the time value of this column (time_t equivalent, seconds since midnight (00:00:00), January 1, 1970, coordinated universal time (UTC) +// ignoring daylight savings +//----------------------------------------------------------------------------- +uint64 CSQLRow::UlTime( int iRowData ) const +{ + Assert( m_VecSQLRowData[iRowData].eColumnType == SQL_TIME ); + return m_VecSQLRowData[iRowData].data.ulTime; +} + + +//----------------------------------------------------------------------------- +// Purpose: returns the boolean value of this column +//----------------------------------------------------------------------------- +bool CSQLRow::BData( int iRowData ) const +{ + Assert( m_VecSQLRowData[iRowData].eColumnType == SQL_INT ); + return (m_VecSQLRowData[iRowData].data.nData == 1); +} + + +//----------------------------------------------------------------------------- +// Purpose: returns the data type contained in this column +//----------------------------------------------------------------------------- +EColumnType CSQLRow::GetEColumnType( int iRowData ) const +{ + return m_VecSQLRowData[iRowData].eColumnType; +} + + +//----------------------------------------------------------------------------- +// 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 CSQLRow::Validate( CValidator &validator, char *pchName ) +{ + validator.Push( "CSQLRow", this, pchName ); + + m_VecSQLRowData.Validate( validator, "m_VecSQLRowData" ); + + m_VecPchStoredStrings.Validate( validator, "m_VecPchStoredStrings" ); + for ( int i = 0; i < m_VecPchStoredStrings.Count(); i++ ) + { + validator.ClaimMemory( m_VecPchStoredStrings[i] ); + } + + validator.Pop(); +} +#endif + + +//----------------------------------------------------------------------------- +// Purpose: constructor +//----------------------------------------------------------------------------- +CResultSet::CResultSet() +:m_SQLTableDescription( "Table" ) +{ + m_pSQLRow = NULL; + m_MySQLRes = NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: run a query on the database +//----------------------------------------------------------------------------- +bool CResultSet::Query( const char *pchQuery, ISQLHelper *pSQLHelper ) +{ + m_SQLTableDescription.Reset(); + + m_pSQLRow = NULL; + + bool bRet = pSQLHelper->BInternalQuery( pchQuery, &m_MySQLRes ); + if ( !bRet || !m_MySQLRes ) + { + m_cSQLField = -1; + m_cSQLRow = -1; + return false; + } + + m_cSQLField = mysql_num_fields( m_MySQLRes ); + m_cSQLRow = mysql_num_rows( m_MySQLRes ); + + MYSQL_FIELD *pMySQLField = mysql_fetch_fields( m_MySQLRes ); + for ( int iMySQLField = 0; iMySQLField < m_cSQLField; iMySQLField++ ) + { + m_SQLTableDescription.AddColumn( pMySQLField[iMySQLField].name, pMySQLField[iMySQLField].type ); + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: destructor +//----------------------------------------------------------------------------- +CResultSet::~CResultSet() +{ + FreeResult(); +} + + +//----------------------------------------------------------------------------- +// Purpose: free any outstanding query +//----------------------------------------------------------------------------- +void CResultSet::FreeResult() +{ + if ( m_MySQLRes ) + { + mysql_free_result( m_MySQLRes ); + m_MySQLRes = NULL; + } + + if ( m_pSQLRow ) + { + delete m_pSQLRow; + m_pSQLRow = NULL; + } + + m_SQLTableDescription.Reset(); +} + + +//----------------------------------------------------------------------------- +// Purpose: number of rows in the result set +//----------------------------------------------------------------------------- +int CResultSet::GetCSQLRow() const +{ + return m_cSQLRow; +} + + +//----------------------------------------------------------------------------- +// Purpose: get the new row from the result set +//----------------------------------------------------------------------------- +const ISQLRow *CResultSet::PSQLRowNextResult() +{ + if ( m_pSQLRow ) + { + delete m_pSQLRow; + m_pSQLRow = NULL; + } + + MYSQL_ROW mySQLRow; + if ( mySQLRow = mysql_fetch_row( m_MySQLRes ) ) + { + m_pSQLRow = new CSQLRow( &mySQLRow, &m_SQLTableDescription, m_cSQLField ); + } + else + { + FreeResult(); // end of result set + } + + return m_pSQLRow; +} + + +//----------------------------------------------------------------------------- +// 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 CResultSet::Validate( CValidator &validator, char *pchName ) +{ + validator.Push( "CResultSet", this, pchName ); + + validator.ClaimMemory( m_MySQLRes ); + if ( m_pSQLRow ) + { + validator.ClaimMemory( m_pSQLRow ); + m_pSQLRow->Validate( validator, "m_pSQLRow" ); + } + + m_SQLTableDescription.Validate( validator, "m_SQLTableDescription" ); + + validator.Pop(); +} +#endif + diff --git a/utils/sqlwrapper/sqlhelpers.h b/utils/sqlwrapper/sqlhelpers.h new file mode 100644 index 0000000..b2c53f6 --- /dev/null +++ b/utils/sqlwrapper/sqlhelpers.h @@ -0,0 +1,162 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//============================================================================= +#include "utlvector.h" + +extern "C" +{ + #include <WinSock.H> + #include "mysql.h" +}; +#include "utlvector.h" + +typedef enum_field_types ESQLFieldType; + +//----------------------------------------------------------------------------- +// Purpose: helper interface for running queries internal to this dll +//----------------------------------------------------------------------------- +class ISQLHelper +{ +public: + // run a sql query on the db + virtual bool BInternalQuery( const char *pchQueryString, MYSQL_RES **ppMySQLRes, bool bRecurse = true ) = 0; +}; + + +//----------------------------------------------------------------------------- +// Purpose: represents the data about a single SQL column +//----------------------------------------------------------------------------- +class CSQLColumn +{ +public: + CSQLColumn() { m_rgchName[0] = 0; } + CSQLColumn( const char *pchName, ESQLFieldType eSQLFieldType ); + virtual const char *PchColumnName() const { return m_rgchName; }; + virtual EColumnType GetEColumnType() const { return m_EColumnType; }; + virtual ESQLFieldType GetESQLFieldType() const { return m_ESQLFieldType; }; + +private: + char m_rgchName[64]; + EColumnType m_EColumnType; + ESQLFieldType m_ESQLFieldType; +}; + + +//----------------------------------------------------------------------------- +// Purpose: encapsulates a table's description +//----------------------------------------------------------------------------- +class CSQLTable : public ISQLTable +{ +public: + CSQLTable( const char *pchName ); + CSQLTable( CSQLTable const &CSQLTable ); // copy constructor + void AddColumn( const char *pchName, ESQLFieldType ESQLFieldType ); + const char *PchName() const { return m_rgchName; }; + virtual int GetCSQLColumn() const { return m_VecSQLColumn.Count(); }; + virtual const char *PchColumnName( int iSQLColumn ) const { return m_VecSQLColumn[iSQLColumn].PchColumnName(); }; + virtual EColumnType GetEColumnType( int iSQLColumn ) const { return m_VecSQLColumn[iSQLColumn].GetEColumnType(); }; + virtual ESQLFieldType GetESQLFieldType( int iSQLColumn ) const { return m_VecSQLColumn[iSQLColumn].GetESQLFieldType(); }; + + void Reset() { m_VecSQLColumn.RemoveAll(); } + +#ifdef DBGFLAG_VALIDATE + void Validate( CValidator &validator, char *pchName ); +#endif + +private: + char m_rgchName[64]; + CUtlVector<CSQLColumn> m_VecSQLColumn; +}; + + +//----------------------------------------------------------------------------- +// Purpose: encapsulates a db's worth of tables +//----------------------------------------------------------------------------- +class CSQLTableSet : public ISQLTableSet +{ +public: + CSQLTableSet() { m_bInit = false; } + bool Init( ISQLHelper *pISQLHelper ); + bool BInit() { return m_bInit; } + virtual int GetCSQLTable() const { return m_VecSQLTable.Count(); }; + virtual const ISQLTable *PSQLTable( int iSQLTable ) const; + +#ifdef DBGFLAG_VALIDATE + void Validate( CValidator &validator, char *pchName ); +#endif + +private: + static EColumnType EColumnTypeFromPchName( const char *pchName ); + CUtlVector<CSQLTable> m_VecSQLTable; + bool m_bInit; +}; + + +//----------------------------------------------------------------------------- +// Purpose: describes a single row in a result set +//----------------------------------------------------------------------------- +class CSQLRow : public ISQLRow +{ +public: + CSQLRow( MYSQL_ROW *pMySQLRow, const ISQLTable *pSQLTableDescription, int cSQLRowData ); + ~CSQLRow(); + virtual int GetCSQLRowData() const { return m_VecSQLRowData.Count(); }; + virtual const char *PchData( int iSQLRowData ) const; + virtual int NData( int iSQLRowData ) const; + virtual uint64 UlData( int iSQLRowData ) const; + virtual float FlData( int iSQLRowData ) const; + virtual uint64 UlTime( int iSQLRowData ) const; + virtual bool BData( int iSQLRowData ) const; + virtual EColumnType GetEColumnType( int iSQLRowData ) const; + +#ifdef DBGFLAG_VALIDATE + void Validate( CValidator &validator, char *pchName ); +#endif + +private: + struct SQLRowData_s + { + union + { + int nData; + float flData; + const char *pchData; + uint64 ulTime; + uint64 ulData; + } data; + EColumnType eColumnType; + }; + + CUtlVector<struct SQLRowData_s> m_VecSQLRowData; + CUtlVector<char *> m_VecPchStoredStrings; +}; + + +//----------------------------------------------------------------------------- +// Purpose: encapsulates a result set from a SQL query +//----------------------------------------------------------------------------- +class CResultSet : public IResultSet +{ +public: + CResultSet(); + ~CResultSet(); + bool Query( const char *pchQuery, ISQLHelper *pISQLHelper ); + virtual int GetCSQLRow() const; + virtual const ISQLRow *PSQLRowNextResult(); + void FreeResult(); + +#ifdef DBGFLAG_VALIDATE + void Validate( CValidator &validator, char *pchName ); +#endif + +private: + MYSQL_RES *m_MySQLRes; + CSQLRow *m_pSQLRow; + CSQLTable m_SQLTableDescription; + int m_cSQLField; + int m_cSQLRow; +}; + 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 diff --git a/utils/sqlwrapper/sqlwrapper.vcproj b/utils/sqlwrapper/sqlwrapper.vcproj new file mode 100644 index 0000000..44b9683 --- /dev/null +++ b/utils/sqlwrapper/sqlwrapper.vcproj @@ -0,0 +1,200 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="7.10" + Name="SQLWrapper" + ProjectGUID="{D66A7F7E-CD38-45CC-951D-E22E5DED6585}" + SccProjectName="Perforce Project" + SccAuxPath="" + SccLocalPath=".." + SccProvider="MSSCCI:Perforce SCM" + Keyword="Win32Proj"> + <Platforms> + <Platform + Name="Win32"/> + </Platforms> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory="..\..\bin\$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="2" + CharacterSet="2"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="..\..\common;..\..\public;..\..;..\..\public\tier0;..\..\public\tier1;..\vmpi_private\mysql\include" + PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;" + MinimalRebuild="TRUE" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + RuntimeTypeInfo="TRUE" + UsePrecompiledHeader="0" + WarningLevel="4" + WarnAsError="TRUE" + Detect64BitPortabilityProblems="FALSE" + DebugInformationFormat="4"/> + <Tool + Name="VCCustomBuildTool" + CommandLine="if exist ..\..\..\game\bin\sqlwrapper.dll attrib -r ..\..\..\game\bin\sqlwrapper.dll +copy "$(TargetDir)"sqlwrapper.dll ..\..\..\game\bin\sqlwrapper.dll +if exist ..\..\..\game\bin\sqlwrapper.pdb attrib -r ..\..\..\game\bin\sqlwrapper.pdb +copy "$(TargetDir)"sqlwrapper.pdb ..\..\..\game\bin +if exist ..\..\..\src\lib\common\sqlwrapper.lib attrib -r ..\..\..\src\lib\common\sqlwrapper.lib +copy "$(TargetDir)"sqlwrapper.lib ..\..\..\src\lib\common\ + +" + Outputs="..\..\..\game\bin\"$(TargetName)".dll"/> + <Tool + Name="VCLinkerTool" + AdditionalDependencies="mswsock.lib winmm.lib ws2_32.lib libmySQL.lib tier0.lib tier1.lib vstdlib.lib" + OutputFile="$(OutDir)/sqlwrapper.dll" + LinkIncremental="2" + AdditionalLibraryDirectories=""../../common/$(IntDir)";../../lib/public;../vmpi_private/mysql/lib/opt" + GenerateDebugInformation="TRUE" + ProgramDatabaseFile="$(OutDir)/sqlwrapper.pdb" + SubSystem="0" + ImportLibrary="$(OutDir)/sqlwrapper.lib" + TargetMachine="1"/> + <Tool + Name="VCMIDLTool"/> + <Tool + Name="VCPostBuildEventTool"/> + <Tool + Name="VCPreBuildEventTool"/> + <Tool + Name="VCPreLinkEventTool"/> + <Tool + Name="VCResourceCompilerTool"/> + <Tool + Name="VCWebServiceProxyGeneratorTool"/> + <Tool + Name="VCXMLDataGeneratorTool"/> + <Tool + Name="VCWebDeploymentTool"/> + <Tool + Name="VCManagedWrapperGeneratorTool"/> + <Tool + Name="VCAuxiliaryManagedWrapperGeneratorTool"/> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory="..\..\bin\$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="2" + CharacterSet="2"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + GlobalOptimizations="TRUE" + InlineFunctionExpansion="2" + EnableIntrinsicFunctions="TRUE" + FavorSizeOrSpeed="1" + OptimizeForProcessor="3" + AdditionalIncludeDirectories="..\..\common;..\..\public;..\..;..\..\public\tier0;..\..\public\tier1;..\vmpi_private\mysql\include" + PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL" + StringPooling="TRUE" + RuntimeLibrary="0" + BufferSecurityCheck="FALSE" + EnableFunctionLevelLinking="FALSE" + EnableEnhancedInstructionSet="1" + RuntimeTypeInfo="TRUE" + UsePrecompiledHeader="0" + WarningLevel="4" + WarnAsError="TRUE" + Detect64BitPortabilityProblems="FALSE" + DebugInformationFormat="3"/> + <Tool + Name="VCCustomBuildTool" + CommandLine="if exist ..\..\..\game\bin\sqlwrapper.dll attrib -r ..\..\..\game\bin\sqlwrapper.dll +copy "$(TargetDir)"sqlwrapper.dll ..\..\..\game\bin\sqlwrapper.dll + +if exist ..\..\..\game\bin\sqlwrapper.pdb attrib -r ..\..\..\game\bin\sqlwrapper.pdb +copy "$(TargetDir)"sqlwrapper.pdb ..\..\..\game\bin\sqlwrapper.pdb + +if exist ..\..\..\src\lib\common\sqlwrapper.lib attrib -r ..\..\..\src\lib\common\sqlwrapper.lib +copy "$(TargetDir)"sqlwrapper.lib ..\..\..\src\lib\common\sqlwrapper.lib + +" + Outputs="..\..\..\game\bin\"$(TargetName)".dll"/> + <Tool + Name="VCLinkerTool" + AdditionalDependencies="mswsock.lib winmm.lib ws2_32.lib libmySQL.lib tier0.lib tier1.lib vstdlib.lib" + OutputFile="$(OutDir)/sqlwrapper.dll" + LinkIncremental="1" + AdditionalLibraryDirectories=""../../common/$(IntDir)";../../lib/public;../vmpi_private/mysql/lib/opt" + GenerateDebugInformation="TRUE" + ProgramDatabaseFile="$(OutDir)/sqlwrapper.pdb" + SubSystem="0" + OptimizeReferences="2" + EnableCOMDATFolding="2" + ImportLibrary="$(OutDir)/sqlwrapper.lib" + TargetMachine="1"/> + <Tool + Name="VCMIDLTool"/> + <Tool + Name="VCPostBuildEventTool"/> + <Tool + Name="VCPreBuildEventTool"/> + <Tool + Name="VCPreLinkEventTool"/> + <Tool + Name="VCResourceCompilerTool"/> + <Tool + Name="VCWebServiceProxyGeneratorTool"/> + <Tool + Name="VCXMLDataGeneratorTool"/> + <Tool + Name="VCWebDeploymentTool"/> + <Tool + Name="VCManagedWrapperGeneratorTool"/> + <Tool + Name="VCAuxiliaryManagedWrapperGeneratorTool"/> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="Source Files" + Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx" + UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"> + <File + RelativePath="..\..\tier1\interface.cpp"> + </File> + <File + RelativePath="..\..\public\tier0\memoverride.cpp"> + </File> + <File + RelativePath=".\sqlhelpers.cpp"> + </File> + <File + RelativePath=".\sqlwrapper.cpp"> + </File> + </Filter> + <Filter + Name="Header Files" + Filter="h;hpp;hxx;hm;inl;inc;xsd" + UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"> + <File + RelativePath="..\..\public\tier1\interface.h"> + </File> + <File + RelativePath="..\..\public\isqlwrapper.h"> + </File> + <File + RelativePath="..\vmpi_private\mysql\include\mysql.h"> + </File> + <File + RelativePath=".\sqlhelpers.h"> + </File> + </Filter> + <Filter + Name="Resource Files" + Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx" + UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"> + </Filter> + </Files> + <Globals> + </Globals> +</VisualStudioProject> |