//========= 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(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