You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
354 lines
14 KiB
354 lines
14 KiB
//====== Copyright (c), Valve Corporation, All rights reserved. =======
|
|
//
|
|
// Purpose: Provides access to SQL at a high level
|
|
//
|
|
//=============================================================================
|
|
|
|
#ifndef SQLACCESS_H
|
|
#define SQLACCESS_H
|
|
#ifdef _WIN32
|
|
#pragma once
|
|
#endif
|
|
|
|
#include "gcsdk/gcsqlquery.h"
|
|
|
|
#include "tier0/memdbgon.h"
|
|
|
|
namespace GCSDK
|
|
{
|
|
class CGCSQLQuery;
|
|
class CGCSQLQueryGroup;
|
|
class CColumnSet;
|
|
class CRecordType;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Provides access to SQL at a high level
|
|
//-----------------------------------------------------------------------------
|
|
class CSQLAccess
|
|
{
|
|
public:
|
|
CSQLAccess( ESchemaCatalog eSchemaCatalog = k_ESchemaCatalogMain );
|
|
~CSQLAccess( );
|
|
|
|
bool BBeginTransaction( const char *pchName );
|
|
bool BCommitTransaction( bool bAllowEmpty = false );
|
|
void RollbackTransaction();
|
|
bool BInTransaction( ) const { return m_bInTransaction; }
|
|
|
|
bool BYieldingExecute( const char *pchName, const char *pchSQLCommand, uint32 *pcRowsAffected = NULL, bool bSpewOnError = true );
|
|
bool BYieldingExecuteString( const char *pchName, const char *pchSQLCommand, CFmtStr1024 *psResult, uint32 *pcRowsAffected = NULL );
|
|
bool BYieldingExecuteScalarInt( const char *pchName, const char *pchSQLCommand, int *pnResult, uint32 *pcRowsAffected = NULL );
|
|
bool BYieldingExecuteScalarIntWithDefault( const char *pchName, const char *pchSQLCommand, int *pnResult, int iDefaultValue, uint32 *pcRowsAffected = NULL );
|
|
bool BYieldingExecuteScalarUint32( const char *pchName, const char *pchSQLCommand, uint32 *punResult, uint32 *pcRowsAffected = NULL );
|
|
bool BYieldingExecuteScalarUint32WithDefault( const char *pchName, const char *pchSQLCommand, uint32 *punResult, uint32 unDefaultValue, uint32 *pcRowsAffected = NULL );
|
|
bool BYieldingWipeTable( int iTable );
|
|
|
|
template <typename TReturn, typename TCast>
|
|
bool BYieldingExecuteSingleResult( const char *pchName, const char *pchSQLCommand, EGCSQLType eType, TReturn *pResult, uint32 *pcRowsAffected );
|
|
template <typename TReturn, typename TCast>
|
|
bool BYieldingExecuteSingleResultWithDefault( const char *pchName, const char *pchSQLCommand, EGCSQLType eType, TReturn *pResult, TReturn defaultValue, uint32 *pcRowsAffected );
|
|
|
|
// manipulating CRecordBase (i.e. CSch...) objects in the database
|
|
bool BYieldingInsertRecord( CRecordBase *pRecordBase );
|
|
bool BYieldingInsertWithIdentity( CRecordBase* pRecordBase ) ;
|
|
bool BYieldingReadRecordWithWhereColumns( CRecordBase *pRecord, const CColumnSet & readSet, const CColumnSet & whereSet );
|
|
|
|
template< typename SchClass_t >
|
|
bool BYieldingReadRecordFromPK( SchClass_t *pRecord );
|
|
template< typename SchClass_t>
|
|
bool BYieldingReadMultipleRecordsWithWhereColumns( CUtlVector< SchClass_t > *pvecRecords, const CColumnSet & whereSet, CUtlVector< SchClass_t > *pvecUnmatchedRecords = NULL );
|
|
template< typename SchClass_t>
|
|
bool BYieldingReadMultipleRecordsWithWhereColumns( CUtlVector< SchClass_t > *pvecRecords, const CColumnSet & readSet, const CColumnSet & whereSet, CUtlVector< SchClass_t > *pvecUnmatchedRecords = NULL );
|
|
template< typename SchClass_t>
|
|
bool BYieldingReadRecordsWithWhereClause( CUtlVector< SchClass_t > *pvecRecords, const char *pchWhereClause, const CColumnSet & readSet, const char *pchTopClause = NULL );
|
|
template< typename SchClass_t>
|
|
bool BYieldingReadRecordsWithQuery( CUtlVector< SchClass_t > *pvecRecords, const char *sQuery, const CColumnSet & readSet );
|
|
bool BYieldingUpdateRecord( const CRecordBase &record, const CColumnSet & whereColumns, const CColumnSet & updateColumns );
|
|
bool BYieldingDeleteRecord( const CRecordBase & record, const CColumnSet & whereColumns );
|
|
|
|
void AddRecordParameters( const CRecordBase &record, const CColumnSet & columnSet );
|
|
|
|
void AddBindParam( const char *pchValue );
|
|
void AddBindParam( const int16 nValue );
|
|
void AddBindParam( const uint16 uValue );
|
|
void AddBindParam( const int32 nValue );
|
|
void AddBindParam( const uint32 uValue );
|
|
void AddBindParam( const uint64 ulValue );
|
|
void AddBindParam( const uint8 *ubValue, const int cubValue );
|
|
void AddBindParam( const float fValue );
|
|
void AddBindParam( const double dValue );
|
|
void ClearParams();
|
|
IGCSQLResultSetList *GetResults();
|
|
|
|
uint32 GetResultSetCount();
|
|
uint32 GetResultSetRowCount( uint32 unResultSet );
|
|
CSQLRecord GetResultRecord( uint32 unResultSet, uint32 unRow );
|
|
|
|
private:
|
|
enum EReadSingleResultResult
|
|
{
|
|
eReadSingle_Error, // something went wrong in the DB or the data was in a format we didn't expect
|
|
eReadSingle_ResultFound, // we found a single result and copied the value -- all is well!
|
|
eReadSingle_UseDefault, // we didn't find any results but we specified a value in advance for this case
|
|
};
|
|
|
|
EReadSingleResultResult BYieldingExecuteSingleResultDataInternal( const char *pchName, const char *pchSQLCommand, EGCSQLType eType, uint8 **pubData, uint32 *punSize, uint32 *pcRowsAffected, bool bHasDefaultValue );
|
|
|
|
private:
|
|
|
|
CGCSQLQuery *CurrentQuery();
|
|
ESchemaCatalog m_eSchemaCatalog;
|
|
CGCSQLQuery *m_pCurrentQuery;
|
|
CGCSQLQueryGroup *m_pQueryGroup;
|
|
bool m_bInTransaction;
|
|
};
|
|
|
|
#define FOR_EACH_SQL_RESULT( sqlAccess, resultSet, record ) \
|
|
for( CSQLRecord record = (sqlAccess).GetResultRecord( resultSet, 0 ); record.IsValid(); record.NextRow() )
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: templatized version of querying for a single value
|
|
//-----------------------------------------------------------------------------
|
|
template <typename TReturn, typename TCast>
|
|
bool CSQLAccess::BYieldingExecuteSingleResult( const char *pchName, const char *pchSQLCommand, EGCSQLType eType, TReturn *pResult, uint32 *pcRowsAffected )
|
|
{
|
|
uint8 *pubData;
|
|
uint32 cubData;
|
|
if( CSQLAccess::BYieldingExecuteSingleResultDataInternal( pchName, pchSQLCommand, eType, &pubData, &cubData, pcRowsAffected, false ) != eReadSingle_ResultFound )
|
|
return false;
|
|
|
|
*pResult = *( (TCast *)pubData );
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: templatized version of querying for a single value
|
|
//-----------------------------------------------------------------------------
|
|
template <typename TReturn, typename TCast>
|
|
bool CSQLAccess::BYieldingExecuteSingleResultWithDefault( const char *pchName, const char *pchSQLCommand, EGCSQLType eType, TReturn *pResult, TReturn defaultValue, uint32 *pcRowsAffected )
|
|
{
|
|
uint8 *pubData;
|
|
uint32 cubData;
|
|
EReadSingleResultResult eResult = CSQLAccess::BYieldingExecuteSingleResultDataInternal( pchName, pchSQLCommand, eType, &pubData, &cubData, pcRowsAffected, true );
|
|
|
|
if ( eResult == eReadSingle_Error )
|
|
return false;
|
|
|
|
if ( eResult == eReadSingle_ResultFound )
|
|
{
|
|
*pResult = *( (TCast *)pubData );
|
|
}
|
|
else
|
|
{
|
|
Assert( eResult == eReadSingle_UseDefault );
|
|
*pResult = defaultValue;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Reads the record with a given PK.
|
|
// Input: pRecordBase - record to read
|
|
// Output: true if successful, false otherwise
|
|
//-----------------------------------------------------------------------------
|
|
template < typename SchClass_t >
|
|
bool CSQLAccess::BYieldingReadRecordFromPK( SchClass_t *pRecord )
|
|
{
|
|
CColumnSet csetWhere = CColumnSet::PrimaryKey< SchClass_t >();
|
|
CColumnSet csetRead = CColumnSet::Inverse( csetWhere );
|
|
return BYieldingReadRecordWithWhereColumns( pRecord, csetRead, csetWhere );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Reads multiple records from the database based on the where columns
|
|
// filled in for each record. If the record is not found in the database
|
|
// it will be removed from pvecRecords. If pvecUnmatchedRecords is
|
|
// provided, it will be populated with the unmatched records removed
|
|
// from pvecRecords
|
|
// Input: pvecRecords - The records to fill in from the database
|
|
// whereSet - The set of columns to query on
|
|
// (optional) pvecUnmatchedRecords - A vector to hold records which
|
|
// are not found in the database
|
|
// Output: true if successful, false otherwise
|
|
//-----------------------------------------------------------------------------
|
|
template< typename SchClass_t>
|
|
bool CSQLAccess::BYieldingReadMultipleRecordsWithWhereColumns( CUtlVector< SchClass_t > *pvecRecords,
|
|
const CColumnSet & whereSet,
|
|
CUtlVector< SchClass_t > *pvecUnmatchedRecords /* = NULL */ )
|
|
{
|
|
CColumnSet readSet( GSchemaFull().GetSchema( SchClass_t::k_iTable ).GetRecordInfo() );
|
|
readSet.MakeInverse( whereSet );
|
|
return BYieldingReadMultipleRecordsWithWhereColumns( pvecRecords, readSet, whereSet, pvecUnmatchedRecords );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Reads multiple records from the database based on the where columns
|
|
// filled in for each record. If the record is not found in the database
|
|
// it will be removed from pvecRecords. If pvecUnmatchedRecords is
|
|
// provided, it will be populated with the unmatched records removed
|
|
// from pvecRecords
|
|
// Input: pvecRecords - The records to fill in from the database
|
|
// readSet - The set of columns to fill in
|
|
// whereSet - The set of columns to query on
|
|
// (optional) pvecUnmatchedRecords - A vector to hold records which
|
|
// are not found in the database
|
|
// Output: true if successful, false otherwise
|
|
//-----------------------------------------------------------------------------
|
|
template< typename SchClass_t>
|
|
bool CSQLAccess::BYieldingReadMultipleRecordsWithWhereColumns( CUtlVector< SchClass_t > *pvecRecords,
|
|
const CColumnSet & readSet,
|
|
const CColumnSet & whereSet,
|
|
CUtlVector< SchClass_t > *pvecUnmatchedRecords /* = NULL */ )
|
|
{
|
|
AssertMsg( !BInTransaction(), "BYieldingReadMultipleRecordsWithWhereColumns is not supported in a transaction" );
|
|
if( BInTransaction() )
|
|
return false;
|
|
|
|
Assert( !readSet.IsEmpty() );
|
|
if ( readSet.IsEmpty() )
|
|
return false;
|
|
|
|
if ( pvecUnmatchedRecords )
|
|
{
|
|
pvecUnmatchedRecords->RemoveAll();
|
|
}
|
|
|
|
// Build the query we'll use for each record
|
|
CFmtStr1024 sStatement, sWhere;
|
|
BuildSelectStatementText( &sStatement, readSet );
|
|
BuildWhereClauseText( &sWhere, whereSet );
|
|
sStatement.Append( " WHERE " );
|
|
sStatement.Append( sWhere );
|
|
|
|
BBeginTransaction( CFmtStr1024( "BYieldingReadMultipleRecordsWithWhereColumns() - %s", sStatement.Access() ) );
|
|
|
|
// Batch this query for each record
|
|
FOR_EACH_VEC( *pvecRecords, i )
|
|
{
|
|
AddRecordParameters( pvecRecords->Element( i ), whereSet );
|
|
if( !BYieldingExecute( NULL, sStatement ) )
|
|
return false;
|
|
}
|
|
|
|
// Actually run the query
|
|
if ( !BCommitTransaction() )
|
|
return false;
|
|
|
|
Assert( GetResultSetCount() == (uint32)pvecRecords->Count() );
|
|
if ( GetResultSetCount() != (uint32)pvecRecords->Count() )
|
|
return false;
|
|
|
|
// Get the results. Reading backwards because if a record doesn't find a match we'll
|
|
// remove it from the list
|
|
FOR_EACH_VEC_BACK( *pvecRecords, i )
|
|
{
|
|
// make sure the types are the same
|
|
IGCSQLResultSet *pResultSet = m_pQueryGroup->GetResults()->GetResultSet( i );
|
|
Assert( pResultSet->GetRowCount() <= 1 );
|
|
if ( pResultSet->GetRowCount() > 1 )
|
|
return false;
|
|
|
|
if( pResultSet->GetRowCount() == 1 )
|
|
{
|
|
// We have a record in this set, read it in
|
|
FOR_EACH_COLUMN_IN_SET( readSet, nColumnIndex )
|
|
{
|
|
EGCSQLType eRecordType = readSet.GetColumnInfo( nColumnIndex ).GetType();
|
|
EGCSQLType eResultType = pResultSet->GetColumnType( nColumnIndex );
|
|
|
|
Assert( eResultType == eRecordType );
|
|
if( eRecordType != eResultType )
|
|
return false;
|
|
}
|
|
|
|
CSQLRecord sqlRecord = GetResultRecord( i, 0 );
|
|
|
|
FOR_EACH_COLUMN_IN_SET( readSet, nColumnIndex )
|
|
{
|
|
uint8 *pubData;
|
|
uint32 cubData;
|
|
|
|
DbgVerify( sqlRecord.BGetColumnData( nColumnIndex, &pubData, (int*)&cubData ) );
|
|
DbgVerify( pvecRecords->Element( i ).BSetField( readSet.GetColumn( nColumnIndex ), pubData, cubData ) );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// This record did not match, remove it and add it to pvecUnmatchedRecords if needed
|
|
if ( pvecUnmatchedRecords )
|
|
{
|
|
pvecUnmatchedRecords->AddToTail( pvecRecords->Element( i ) );
|
|
}
|
|
|
|
pvecRecords->Remove( i );
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Reads a list of records from the DB according to the specified where
|
|
// clause
|
|
// Input: pRecordBase - record to insert
|
|
// Output: true if successful, false otherwise
|
|
//-----------------------------------------------------------------------------
|
|
template< typename SchClass_t>
|
|
bool CSQLAccess::BYieldingReadRecordsWithWhereClause( CUtlVector< SchClass_t > *pvecRecords, const char *pchWhereClause, const CColumnSet & readSet, const char *pchTopClause )
|
|
{
|
|
AssertMsg( !BInTransaction(), "BYieldingReadRecordsWithWhereClause is not supported in a transaction" );
|
|
if( BInTransaction() )
|
|
return false;
|
|
|
|
Assert( !readSet.IsEmpty() );
|
|
CFmtStr1024 sStatement;
|
|
BuildSelectStatementText( &sStatement, readSet, pchTopClause );
|
|
Assert( pchWhereClause && *pchWhereClause );
|
|
if( !pchWhereClause || !(*pchWhereClause) )
|
|
return false;
|
|
|
|
CUtlString sFullStatement = sStatement.Access();
|
|
sFullStatement += " WHERE ";
|
|
sFullStatement += pchWhereClause;
|
|
|
|
return BYieldingReadRecordsWithQuery< SchClass_t >( pvecRecords, sFullStatement, readSet );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Inserts a new record into the DB and reads non-insertable fields back
|
|
// into the record.
|
|
// Input: pRecordBase - record to insert
|
|
// Output: true if successful, false otherwise
|
|
//-----------------------------------------------------------------------------
|
|
template< typename SchClass_t>
|
|
bool CSQLAccess::BYieldingReadRecordsWithQuery( CUtlVector< SchClass_t > *pvecRecords, const char *sQuery, const CColumnSet & readSet )
|
|
{
|
|
AssertMsg( !BInTransaction(), "BYieldingReadRecordsWithQuery is not supported in a transaction" );
|
|
if( BInTransaction() )
|
|
return false;
|
|
|
|
Assert(!readSet.IsEmpty() );
|
|
if( !BYieldingExecute( NULL, sQuery ) )
|
|
return false;
|
|
|
|
Assert( GetResultSetCount() == 1 );
|
|
if ( GetResultSetCount() != 1 )
|
|
return false;
|
|
|
|
// make sure the types are the same
|
|
IGCSQLResultSet *pResultSet = m_pQueryGroup->GetResults()->GetResultSet( 0 );
|
|
return CopyResultToSchVector( pResultSet, readSet, pvecRecords );
|
|
}
|
|
|
|
} // namespace GCSDK
|
|
|
|
#include "tier0/memdbgoff.h"
|
|
|
|
#endif // SQLACCESS_H
|