This commit is contained in:
FluorescentCIAAfricanAmerican
2020-04-22 12:56:21 -04:00
commit 3bf9df6b27
15370 changed files with 5489726 additions and 0 deletions
+368
View File
@@ -0,0 +1,368 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Sets of columns in SQL queries
//
// $NoKeywords: $
//=============================================================================
#include "stdafx.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
namespace GCSDK
{
//-----------------------------------------------------------------------------
// Purpose: Constructs a column set with no columns in it
//-----------------------------------------------------------------------------
CColumnSet::CColumnSet( const CRecordInfo *pRecordInfo )
: m_pRecordInfo( pRecordInfo )
{
}
//-----------------------------------------------------------------------------
// Purpose: Constructs a column set with a single column in it
// Inputs: nColumn - the column to add
//-----------------------------------------------------------------------------
CColumnSet::CColumnSet( const CRecordInfo *pRecordInfo, int col1 )
: m_pRecordInfo( pRecordInfo )
{
m_vecColumns.AddToTail( col1 );
}
CColumnSet::CColumnSet( const CRecordInfo *pRecordInfo, int col1, int col2 )
: m_pRecordInfo( pRecordInfo )
{
m_vecColumns.EnsureCapacity( 2 );
m_vecColumns.AddToTail( col1 );
m_vecColumns.AddToTail( col2 );
}
CColumnSet::CColumnSet( const CRecordInfo *pRecordInfo, int col1, int col2, int col3 )
: m_pRecordInfo( pRecordInfo )
{
m_vecColumns.EnsureCapacity( 3 );
m_vecColumns.AddToTail( col1 );
m_vecColumns.AddToTail( col2 );
m_vecColumns.AddToTail( col3 );
}
CColumnSet::CColumnSet( const CRecordInfo *pRecordInfo, int col1, int col2, int col3, int col4 )
: m_pRecordInfo( pRecordInfo )
{
m_vecColumns.EnsureCapacity( 4 );
m_vecColumns.AddToTail( col1 );
m_vecColumns.AddToTail( col2 );
m_vecColumns.AddToTail( col3 );
m_vecColumns.AddToTail( col4 );
}
CColumnSet::CColumnSet( const CRecordInfo *pRecordInfo, int col1, int col2, int col3, int col4, int col5 )
: m_pRecordInfo( pRecordInfo )
{
m_vecColumns.EnsureCapacity( 5 );
m_vecColumns.AddToTail( col1 );
m_vecColumns.AddToTail( col2 );
m_vecColumns.AddToTail( col3 );
m_vecColumns.AddToTail( col4 );
m_vecColumns.AddToTail( col5 );
}
CColumnSet::CColumnSet( const CRecordInfo *pRecordInfo, int col1, int col2, int col3, int col4, int col5, int col6 )
: m_pRecordInfo( pRecordInfo )
{
m_vecColumns.EnsureCapacity( 6 );
m_vecColumns.AddToTail( col1 );
m_vecColumns.AddToTail( col2 );
m_vecColumns.AddToTail( col3 );
m_vecColumns.AddToTail( col4 );
m_vecColumns.AddToTail( col5 );
m_vecColumns.AddToTail( col6 );
}
CColumnSet::CColumnSet( const CRecordInfo *pRecordInfo, int col1, int col2, int col3, int col4, int col5, int col6, int col7 )
: m_pRecordInfo( pRecordInfo )
{
m_vecColumns.EnsureCapacity( 7 );
m_vecColumns.AddToTail( col1 );
m_vecColumns.AddToTail( col2 );
m_vecColumns.AddToTail( col3 );
m_vecColumns.AddToTail( col4 );
m_vecColumns.AddToTail( col5 );
m_vecColumns.AddToTail( col6 );
m_vecColumns.AddToTail( col7 );
}
CColumnSet::CColumnSet( const CRecordInfo *pRecordInfo, int col1, int col2, int col3, int col4, int col5, int col6, int col7, int col8 )
: m_pRecordInfo( pRecordInfo )
{
m_vecColumns.EnsureCapacity( 8 );
m_vecColumns.AddToTail( col1 );
m_vecColumns.AddToTail( col2 );
m_vecColumns.AddToTail( col3 );
m_vecColumns.AddToTail( col4 );
m_vecColumns.AddToTail( col5 );
m_vecColumns.AddToTail( col6 );
m_vecColumns.AddToTail( col7 );
m_vecColumns.AddToTail( col8 );
}
//-----------------------------------------------------------------------------
// Purpose: Copy constructor
//-----------------------------------------------------------------------------
CColumnSet::CColumnSet( const CColumnSet & rhs )
{
MEM_ALLOC_CREDIT_("CColumnSet");
m_vecColumns.CopyArray( rhs.m_vecColumns.Base(), rhs.m_vecColumns.Count() );
m_pRecordInfo = rhs.m_pRecordInfo;
}
//-----------------------------------------------------------------------------
// Purpose: Assignment operator
//-----------------------------------------------------------------------------
CColumnSet & CColumnSet::operator=( const CColumnSet & rhs )
{
MEM_ALLOC_CREDIT_("CColumnSet");
m_vecColumns.CopyArray( rhs.m_vecColumns.Base(), rhs.m_vecColumns.Count() );
m_pRecordInfo = rhs.m_pRecordInfo;
return *this;
}
//-----------------------------------------------------------------------------
// Purpose: Addition operator. lhs ColumnSet will be a union of the two
// ColumnSets
//-----------------------------------------------------------------------------
CColumnSet & CColumnSet::operator+=( const CColumnSet & rhs )
{
Assert( this->GetRecordInfo() == rhs.GetRecordInfo() );
FOR_EACH_COLUMN_IN_SET( rhs, i )
{
BAddColumn( rhs.GetColumn( i ) );
}
return *this;
}
//-----------------------------------------------------------------------------
// Purpose: Addition operator. Returns a union of lhs and rhs
//-----------------------------------------------------------------------------
const CColumnSet CColumnSet::operator+( const CColumnSet & rhs ) const
{
return CColumnSet( *this ) += rhs;
}
//-----------------------------------------------------------------------------
// Purpose: Adds a column to the set if it is
// Inputs: nColumn - THe column to add
//-----------------------------------------------------------------------------
void CColumnSet::BAddColumn( int nColumn )
{
if( nColumn >= 0 && nColumn < m_pRecordInfo->GetNumColumns() )
{
//not sure best way to handle the 'is already set case'
if( !IsSet( nColumn ) )
m_vecColumns.AddToTail( nColumn );
}
else
{
AssertMsg3( false, "Attempting to set an out of range column on schema type %s, %d (of %d)", GetRecordInfo()->GetName(), nColumn, m_pRecordInfo->GetNumColumns() );
}
}
//-----------------------------------------------------------------------------
// Purpose: Removes a column from the set
// Inputs: nColumn - THe column to remove
//-----------------------------------------------------------------------------
void CColumnSet::BRemoveColumn( int nColumn )
{
m_vecColumns.FindAndRemove( nColumn );
}
//-----------------------------------------------------------------------------
// Purpose: Returns true if a column is in the set
// Inputs: nColumn - THe column to test
//-----------------------------------------------------------------------------
bool CColumnSet::IsSet( int nColumn ) const
{
int nIndex = m_vecColumns.Find( nColumn );
return m_vecColumns.IsValidIndex( nIndex );
}
//-----------------------------------------------------------------------------
// Purpose: Returns the number of columns in the set
//-----------------------------------------------------------------------------
uint32 CColumnSet::GetColumnCount() const
{
return m_vecColumns.Count();
}
//-----------------------------------------------------------------------------
// Purpose: Returns the column index of the Nth column in the set
// Inputs: nIndex - the position in the set to return a column index for.
//-----------------------------------------------------------------------------
int CColumnSet::GetColumn( int nIndex ) const
{
return m_vecColumns[nIndex];
}
//-----------------------------------------------------------------------------
// Purpose: Returns a CColumnInfo object for the nth column in the set
// Inputs: nIndex - the position in the set to return a column info for.
//-----------------------------------------------------------------------------
const CColumnInfo & CColumnSet::GetColumnInfo( int nIndex ) const
{
return m_pRecordInfo->GetColumnInfo( GetColumn( nIndex ) );
}
//-----------------------------------------------------------------------------
// Purpose: Empties the column set
//-----------------------------------------------------------------------------
void CColumnSet::MakeEmpty()
{
m_vecColumns.RemoveAll();
}
//-----------------------------------------------------------------------------
// Purpose: Makes the column set be the full set of all columns in the record info
//-----------------------------------------------------------------------------
void CColumnSet::MakeFull()
{
MakeEmpty();
const int nNumColumns = m_pRecordInfo->GetNumColumns();
m_vecColumns.EnsureCapacity( nNumColumns );
for( int nColumn = 0; nColumn < m_pRecordInfo->GetNumColumns(); nColumn++ )
{
//do a direct add to avoid the exponential cost since we know we won't have conflicts
m_vecColumns.AddToTail( nColumn );
}
}
//-----------------------------------------------------------------------------
// Purpose: Makes the column set be the full set of all insertable columns in
// the record info
//-----------------------------------------------------------------------------
void CColumnSet::MakeInsertable()
{
MakeEmpty();
for( int nColumn = 0; nColumn < m_pRecordInfo->GetNumColumns(); nColumn++ )
{
const CColumnInfo & columnInfo = m_pRecordInfo->GetColumnInfo( nColumn );
if( columnInfo.BIsInsertable() )
{
//do a direct add to avoid the exponential cost since we know we won't have conflicts
m_vecColumns.AddToTail( nColumn );
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Makes the column set be the full set of all noninsertable columns in
// the record info
//-----------------------------------------------------------------------------
void CColumnSet::MakeNoninsertable()
{
MakeEmpty();
for( int nColumn = 0; nColumn < m_pRecordInfo->GetNumColumns(); nColumn++ )
{
const CColumnInfo & columnInfo = m_pRecordInfo->GetColumnInfo( nColumn );
if( !columnInfo.BIsInsertable() )
{
//do a direct add to avoid the exponential cost since we know we won't have conflicts
m_vecColumns.AddToTail( nColumn );
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Makes the column set be the full set of all primary key columns in
// the record info
//-----------------------------------------------------------------------------
void CColumnSet::MakePrimaryKey()
{
MakeEmpty();
for( int nColumn = 0; nColumn < m_pRecordInfo->GetNumColumns(); nColumn++ )
{
const CColumnInfo & columnInfo = m_pRecordInfo->GetColumnInfo( nColumn );
if( columnInfo.BIsPrimaryKey() )
{
//do a direct add to avoid the exponential cost since we know we won't have conflicts
m_vecColumns.AddToTail( nColumn );
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Makes the column set be the full set of all primary key columns in
// the record info
//-----------------------------------------------------------------------------
void CColumnSet::MakeInverse( const CColumnSet & columnSet )
{
MakeEmpty();
for( int nColumn = 0; nColumn < m_pRecordInfo->GetNumColumns(); nColumn++ )
{
if( !columnSet.IsSet( nColumn ) )
{
//do a direct add to avoid the exponential cost since we know we won't have conflicts
m_vecColumns.AddToTail( nColumn );
}
}
}
//-----------------------------------------------------------------------------
// determines if the current column set has all fields set. Useful for detection of new columns being added to the schema
//-----------------------------------------------------------------------------
bool CColumnSet::BAreAllFieldsSet() const
{
for( int nColumn = 0; nColumn < m_pRecordInfo->GetNumColumns(); nColumn++ )
{
if( !IsSet( nColumn ) )
return false;
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Returns a Column Set which is the inverse of the given column set
// STATIC - Difference from MakeInverse is that it has a return value
//-----------------------------------------------------------------------------
CColumnSet CColumnSet::Inverse( const CColumnSet & columnSet )
{
CColumnSet set( columnSet.GetRecordInfo() );
set.MakeInverse( columnSet );
return set;
}
//-----------------------------------------------------------------------------
// Purpose: Claims the memory for CColumnSet
//-----------------------------------------------------------------------------
#ifdef DBGFLAG_VALIDATE
void CColumnSet::Validate( CValidator &validator, const char *pchName )
{
// these are INSIDE the function instead of outside so the interface
// doesn't change
VALIDATE_SCOPE();
ValidateObj( m_vecColumns );
}
#endif
} // namespace GCSDK
+856
View File
@@ -0,0 +1,856 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================
#include "stdafx.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
namespace GCSDK
{
//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
CRecordBase::~CRecordBase()
{
Cleanup();
}
//-----------------------------------------------------------------------------
// Purpose: Copy constructor
// Input: that - CRecord to copy from
//-----------------------------------------------------------------------------
CRecordBase::CRecordBase( const CRecordBase &that )
{
*this = that;
}
//-----------------------------------------------------------------------------
// Purpose: Assignment operator - COPIES the record data
// Input: that - CRecord to copy from
//-----------------------------------------------------------------------------
CRecordBase& CRecordBase::operator = ( const CRecordBase & that )
{
Assert( GetITable() == that.GetITable() );
// COPY that record
Copy( that );
return *this;
}
//-----------------------------------------------------------------------------
// Purpose: Copies the data in the record. This is overridden by CRecordVar and
// CRecordExternal
// Input: that - CRecord to copy from
//-----------------------------------------------------------------------------
void CRecordBase::Copy( const CRecordBase & that )
{
Cleanup();
Q_memcpy( PubRecordFixed(), that.PubRecordFixed(), GetPSchema()->CubRecordFixed() );
}
//-----------------------------------------------------------------------------
// Purpose: Return the record info for this record's schema
//-----------------------------------------------------------------------------
const CRecordInfo *CRecordBase::GetPRecordInfo() const
{
return GetPSchema()->GetRecordInfo();
}
//-----------------------------------------------------------------------------
// Purpose: Copies the data in the var record.
// Input: that - CRecord to copy from
//-----------------------------------------------------------------------------
void CRecordVar::Copy( const CRecordBase & baseThat )
{
const CRecordVar & that = (const CRecordVar &)baseThat;
// COPY that record
Cleanup();
m_pSchema = that.m_pSchema;
Q_memcpy( PubRecordFixed(), that.PubRecordFixed(), GetPSchema()->CubRecordFixed() );
SetFlag( k_EAllocatedVarBlock, false );
if ( VarFieldBlockInfo_t *pVarBlockInfo = GetPSchema()->PVarFieldBlockInfoFromRecord( PubRecordFixed() ) )
{
if ( pVarBlockInfo->m_cubBlock )
{
void *pvNewBlock = malloc( pVarBlockInfo->m_cubBlock );
Q_memcpy( pvNewBlock, pVarBlockInfo->m_pubBlock, pVarBlockInfo->m_cubBlock );
pVarBlockInfo->m_pubBlock = ( uint8 * )pvNewBlock;
SetFlag( k_EAllocatedVarBlock, true );
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Copies the data in the var record.
// Input: that - CRecord to copy from
//-----------------------------------------------------------------------------
void CRecordExternal::Copy( const CRecordBase & baseThat )
{
const CRecordExternal & that = (const CRecordExternal &)baseThat;
Cleanup();
m_pSchema = that.m_pSchema;
m_pubRecordFixedExternal = ( uint8 * )malloc( m_pSchema->CubRecordFixed() );
Q_memcpy( m_pubRecordFixedExternal, that.PubRecordFixed(), m_pSchema->CubRecordFixed() );
SetFlag( k_EAllocatedFixed, true );
SetFlag( k_EAllocatedVarBlock, false );
if ( VarFieldBlockInfo_t *pVarBlockInfo = m_pSchema->PVarFieldBlockInfoFromRecord( PubRecordFixed() ) )
{
if ( pVarBlockInfo->m_cubBlock )
{
void *pvNewBlock = malloc( pVarBlockInfo->m_cubBlock );
Q_memcpy( pvNewBlock, pVarBlockInfo->m_pubBlock, pVarBlockInfo->m_cubBlock );
pVarBlockInfo->m_pubBlock = ( uint8 * )pvNewBlock;
SetFlag( k_EAllocatedVarBlock, true );
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Initialize to an empty record
// Input: pSchema - Schema for the record this will hold
//-----------------------------------------------------------------------------
void CRecordExternal::Init( CSchema *pSchema )
{
Cleanup();
m_pSchema = pSchema;
m_pubRecordFixedExternal = ( uint8 * )malloc( m_pSchema->CubRecordFixed() );
Q_memset( m_pubRecordFixedExternal, 0, m_pSchema->CubRecordFixed() );
SetFlag( k_EAllocatedFixed, true );
}
//-----------------------------------------------------------------------------
// Purpose: Initialize pointing to a record expanded in memory
// Input: pSchema - Schema for the record this will hold
// pubRecord - Pointer to fixed record data
// bTakeOwnership - Should we delete the record when destroyed
// Output: Size of the record's data
//-----------------------------------------------------------------------------
int CRecordBase::InitFromBytes( uint8 *pubRecord )
{
Cleanup();
Q_memcpy( PubRecordFixed(), pubRecord, GetPSchema()->CubRecordFixed() );
int cubRead = GetPSchema()->CubRecordFixed();
return cubRead;
}
int CRecordVar::InitFromBytes( uint8 *pubRecord )
{
Cleanup();
Q_memcpy( PubRecordFixed(), pubRecord, GetPSchema()->CubRecordFixed() );
int cubRead = GetPSchema()->CubRecordFixed();
if ( VarFieldBlockInfo_t *pVarBlockInfo = GetPSchema()->PVarFieldBlockInfoFromRecord( PubRecordFixed() ) )
{
if ( pVarBlockInfo->m_cubBlock )
{
void *pvNewBlock = malloc( pVarBlockInfo->m_cubBlock );
Q_memcpy( pvNewBlock, pVarBlockInfo->m_pubBlock, pVarBlockInfo->m_cubBlock );
pVarBlockInfo->m_pubBlock = ( uint8 * )pvNewBlock;
SetFlag( k_EAllocatedVarBlock, true );
cubRead += pVarBlockInfo->m_cubBlock;
}
}
return cubRead;
}
int CRecordExternal::Init( CSchema *pSchema, uint8 *pubRecord, bool bTakeOwnership )
{
m_pSchema = pSchema;
m_pubRecordFixedExternal = pubRecord;
SetFlag( k_EAllocatedFixed, bTakeOwnership );
SetFlag( k_EAllocatedVarBlock, bTakeOwnership );
int cubRead = m_pSchema->CubRecordFixed() + CubRecordVarBlock();
return cubRead;
}
CSchema *CRecordBase::GetPSchema()
{
return GetPSchemaImpl();
}
CSchema *CRecordBase::GetPSchemaImpl()
{
CSchema *pSchema = NULL;
int i = GetITable();
if ( i != -1 )
pSchema = &GSchemaFull().GetSchema( i );
return pSchema;
}
//-----------------------------------------------------------------------------
// Purpose: Render a field to a buffer
// Input: unColumn - field to render
// cchBuffer - size of render buffer
// pchBuffer - buffer to render into
//-----------------------------------------------------------------------------
void CRecordBase::RenderField( uint32 unColumn, int cchBuffer, char *pchBuffer ) const
{
Q_strncpy( pchBuffer, "", cchBuffer );
uint8 *pubData;
uint32 cubData;
if ( !BGetField( unColumn, &pubData, &cubData ) )
return;
// Get the column info and figure out how to interpret the data
ConvertFieldToText( GetPRecordInfo()->GetColumnInfo( unColumn ).GetType(), pubData, cubData, pchBuffer, cchBuffer, false );
}
//-----------------------------------------------------------------------------
// Purpose: Reset to base state, freeing any memory we are responsible for
//-----------------------------------------------------------------------------
void CRecordBase::Cleanup()
{
}
void CRecordVar::Cleanup()
{
// Must do this before freeing memory that encloses it
// (eg releasing the net packet)
if ( BFlagSet( k_EAllocatedVarBlock ) )
{
void *pvVarBlock = m_pSchema->PVarFieldBlockInfoFromRecord( PubRecordFixed() )->m_pubBlock;
free( pvVarBlock );
m_pSchema->PVarFieldBlockInfoFromRecord( PubRecordFixed() )->m_pubBlock = NULL;
SetFlag( k_EAllocatedVarBlock, false );
}
}
void CRecordExternal::Cleanup()
{
// clean up the variable-length memory we might have allocated
if ( BFlagSet( k_EAllocatedVarBlock ) )
{
void *pvVarBlock = m_pSchema->PVarFieldBlockInfoFromRecord( PubRecordFixed() )->m_pubBlock;
free( pvVarBlock );
m_pSchema->PVarFieldBlockInfoFromRecord( PubRecordFixed() )->m_pubBlock = NULL;
SetFlag( k_EAllocatedVarBlock, false );
}
// clean up the external memory we might have allocated
if ( BFlagSet( k_EAllocatedFixed ) )
free( m_pubRecordFixedExternal );
SetFlag( k_EAllocatedFixed, false );
m_pubRecordFixedExternal = NULL;
// clean up the lowest layer, not calling CRecordVar
CRecordBase::Cleanup();
}
//-----------------------------------------------------------------------------
// Purpose: Deserializes a block of memory into this record
// Input: pubData - Memory block to deserialize from
//-----------------------------------------------------------------------------
void CRecordExternal::DeSerialize( uint8 *pubData )
{
InitFromBytes( pubData );
}
//-----------------------------------------------------------------------------
// Purpose: Calculates the size of this record when serialized
// Output: Size of serialized message
//-----------------------------------------------------------------------------
uint32 CRecordBase::CubSerialized()
{
return CubRecordFixed() + CubRecordVarBlock();
}
//-----------------------------------------------------------------------------
// Purpose: Get pointer to fixed part of record
// Output: pubRecordFixed
//-----------------------------------------------------------------------------
uint8* CRecordBase::PubRecordFixed()
{
return ( uint8 * )( this + 1 );
}
uint8* CRecordExternal::PubRecordFixed()
{
Assert( m_pubRecordFixedExternal );
return m_pubRecordFixedExternal;
}
uint8* CRecordVar::PubRecordFixed()
{
return ( uint8 * )( this + 1 );
}
//-----------------------------------------------------------------------------
// Purpose: Get pointer to fixed part of record
// Output: pubRecordFixed
//-----------------------------------------------------------------------------
const uint8* CRecordBase::PubRecordFixed() const
{
return const_cast<CRecordBase *>( this )->PubRecordFixed();
}
const uint8* CRecordVar::PubRecordFixed() const
{
return const_cast<CRecordVar *>( this )->PubRecordFixed();
}
const uint8* CRecordExternal::PubRecordFixed() const
{
return const_cast<CRecordExternal *>( this )->PubRecordFixed();
}
//-----------------------------------------------------------------------------
// Purpose: Get size of fixed part of record
// Output: size in bytes of fixed part
//-----------------------------------------------------------------------------
uint32 CRecordBase::CubRecordFixed() const
{
return GetPSchema()->CubRecordFixed();
}
//-----------------------------------------------------------------------------
// Purpose: Get pointer to variable part of record
// Output: Pointer to variable-length block -- may be NULL if this record
// has no var-length fields or they are all empty
//-----------------------------------------------------------------------------
uint8* CRecordBase::PubRecordVarBlock()
{
VarFieldBlockInfo_t *pVarFieldBlockInfo = GetPSchema()->PVarFieldBlockInfoFromRecord( PubRecordFixed() );
if ( pVarFieldBlockInfo )
{
return pVarFieldBlockInfo->m_pubBlock;
}
else
{
return NULL;
}
}
//-----------------------------------------------------------------------------
// Purpose: Get pointer to variable part of record
// Output: Pointer to variable-length block -- may be NULL if this record
// has no var-length fields or they are all empty
//-----------------------------------------------------------------------------
const uint8* CRecordBase::PubRecordVarBlock() const
{
return const_cast<CRecordBase *>( this )->PubRecordVarBlock();
}
//-----------------------------------------------------------------------------
// Purpose: Get size of variable part of record
// Output: Size in bytes of var-length block - may be zero if this record
// has no var-length fields or they are all empty
//-----------------------------------------------------------------------------
uint32 CRecordBase::CubRecordVarBlock() const
{
VarFieldBlockInfo_t *pVarFieldBlockInfo = GetPSchema()->PVarFieldBlockInfoFromRecord( PubRecordFixed() );
if ( pVarFieldBlockInfo )
{
return pVarFieldBlockInfo->m_cubBlock;
}
else
{
return 0;
}
}
//-----------------------------------------------------------------------------
// Purpose: Get size of variable part of record
// Output: Size in bytes of var-length block - may be zero if this record
// has no var-length fields or they are all empty
//-----------------------------------------------------------------------------
bool CRecordBase::BAssureRecordVarStorage( uint32 cVariableBytes )
{
// get the variable field block
VarFieldBlockInfo_t *pVarFieldBlockInfo = GetPSchema()->PVarFieldBlockInfoFromRecord( PubRecordFixed() );
if ( pVarFieldBlockInfo )
{
// if we have it, see if it's got enough storage
if ( pVarFieldBlockInfo->m_cubBlock >= cVariableBytes )
{
// already there
return true;
}
// allocate it
uint8* pubData = (uint8*) malloc( cVariableBytes );
if ( pubData == NULL )
return false;
// do we have something right now?
if ( pVarFieldBlockInfo->m_cubBlock != 0 )
{
// sure do. copy it over.
Q_memcpy( pubData, pVarFieldBlockInfo->m_pubBlock, pVarFieldBlockInfo->m_cubBlock );
// free what was there
free( pVarFieldBlockInfo->m_pubBlock );
}
// hook up our buffer
pVarFieldBlockInfo->m_cubBlockFree = cVariableBytes - pVarFieldBlockInfo->m_cubBlock;
pVarFieldBlockInfo->m_cubBlock = cVariableBytes;
pVarFieldBlockInfo->m_pubBlock = pubData;
return true;
}
else
{
// we don't have one;
// we've got no variable length fields, and so can't preallocate for them!
return false;
}
}
//-----------------------------------------------------------------------------
// Purpose: Initialize this whole record to random data
// Input: unPrimaryIndex - Primary index to set
//-----------------------------------------------------------------------------
void CRecordExternal::InitRecordRandom( uint32 unPrimaryIndex )
{
bool bRealloced = false;
GetPSchema()->InitRecordRandom( PubRecordFixed(), unPrimaryIndex, &bRealloced, BFlagSet( k_EAllocatedVarBlock ) );
if ( bRealloced )
SetFlag( k_EAllocatedVarBlock, true );
}
//-----------------------------------------------------------------------------
// Purpose: Set a field in this record to random bits
// Input: iField - Field to set
//-----------------------------------------------------------------------------
void CRecordExternal::SetFieldRandom( int iField )
{
bool bRealloced = false;
GetPSchema()->SetFieldRandom( PubRecordFixed(), iField, &bRealloced, BFlagSet( k_EAllocatedVarBlock ) );
if ( bRealloced )
SetFlag( k_EAllocatedVarBlock, true );
}
//-----------------------------------------------------------------------------
// Purpose: Get a field (var or fixed) from this record
// Input: iField - Field to set
// ppubData - Receives pointer to fields data
// pcubField - Receives count of bytes of data (will count the null for strings)
// Output: true if succeeds
//-----------------------------------------------------------------------------
bool CRecordBase::BGetField( int iField, uint8 **ppubData, uint32 *pcubField ) const
{
return GetPSchema()->BGetFieldData( PubRecordFixed(), iField, ppubData, pcubField );
}
//-----------------------------------------------------------------------------
// Purpose: Sets the data for a field, whether fixed or variable length
// Input: iField - index of field to set
// pubData - pointer to field data to copy from
// cubData - size in bytes of that data
// Output: true if successful
//-----------------------------------------------------------------------------
bool CRecordBase::BSetField( int iField, void *pvData, uint32 cubData )
{
bool bRealloced = false;
bool bResult = BSetField( iField, pvData, cubData, &bRealloced );
Assert( !bRealloced );
return bResult;
}
bool CRecordBase::BSetField( int iField, void *pvData, uint32 cubData, bool *pbRealloced )
{
uint8 *pubData = reinterpret_cast<uint8 *>( pvData );
if ( !GetPSchema()->BSetFieldData( PubRecordFixed(), iField, pubData, cubData, pbRealloced ) )
return false;
return true;
}
bool CRecordVar::BSetField( int iField, void *pvData, uint32 cubData )
{
bool bRealloced = false;
bool bResult = CRecordBase::BSetField( iField, pvData, cubData, &bRealloced );
if ( bRealloced )
SetFlag( k_EAllocatedVarBlock, true );
return bResult;
}
//-----------------------------------------------------------------------------
// Purpose: Erases a field, setting it to 0 length (if possible) and filling with nulls
// Input: iField - index of field to wipe
// NOTE: This relies on CSchema::BSetFieldData nulling out the rest of a field when it is set to 0 length!
//-----------------------------------------------------------------------------
void CRecordBase::WipeField( int iField )
{
bool bRealloced = false;
// Empty Data
uint32 un = 0;
// Length should be 0, except for non-variable length strings where length should be 1 (for an empty string "")
int cub = 0;
Field_t &field = GetPSchema()->GetField( iField );
Assert( !field.BIsVariableLength() );
if ( field.BIsStringType() )
cub = 1;
GetPSchema()->BSetFieldData( PubRecordFixed(), iField, ( uint8 * ) &un, cub, &bRealloced );
Assert( !bRealloced );
}
void CRecordVar::WipeField( int iField )
{
bool bRealloced = false;
// Empty Data
uint32 un = 0;
// Length should be 0, except for non-variable length strings where length should be 1 (for an empty string "")
int cub = 0;
Field_t &field = GetPSchema()->GetField( iField );
if ( field.BIsStringType() && !field.BIsVariableLength() )
cub = 1;
GetPSchema()->BSetFieldData( PubRecordFixed(), iField, ( uint8 * ) &un, cub, &bRealloced );
if ( bRealloced )
SetFlag( k_EAllocatedVarBlock, true );
}
//-----------------------------------------------------------------------------
// Purpose: Get a string field - will return empty string instead of NULL if field has no datas
// Input: iField - Field to get
// pcubField - Receives count of bytes of data (will count the null for strings)
// Output: const pointer to string data (to an empty string if no data)
//-----------------------------------------------------------------------------
const char * CRecordBase::GetStringField( int iField, uint32 *pcubField )
{
uint8 * pubData = NULL;
*pcubField = 0;
if ( BGetField( iField, &pubData, pcubField ) && *pcubField > 0 )
return ( const char * ) pubData;
else
return "";
}
//-----------------------------------------------------------------------------
// Purpose: Get an int field
// Input: iField - Field to get
// Output: Int (0 if no data)
//-----------------------------------------------------------------------------
int CRecordBase::GetInt( int iField )
{
return ( int ) GetUint32( iField );
}
//-----------------------------------------------------------------------------
// Purpose: Get a uint16 field
// Input: iField - Field to get
// Output: uint16 (0 if no data)
//-----------------------------------------------------------------------------
uint16 CRecordBase::GetUint16( int iField )
{
uint8 * pubData = NULL;
uint32 cubField = 0;
DbgVerify( BGetField( iField, &pubData, &cubField ) );
Assert( 0 < cubField );
if ( NULL != pubData )
return *( uint16 * ) pubData;
else
return 0;
}
//-----------------------------------------------------------------------------
// Purpose: Get a uint32 field
// Input: iField - Field to get
// Output: uint32 (0 if no data)
//-----------------------------------------------------------------------------
uint32 CRecordBase::GetUint32( int iField )
{
uint8 * pubData = NULL;
uint32 cubField = 0;
DbgVerify( BGetField( iField, &pubData, &cubField ) );
Assert( 0 < cubField );
if ( NULL != pubData )
return *( uint32 * ) pubData;
else
return 0;
}
//-----------------------------------------------------------------------------
// Purpose: Get a uint64 field
// Input: iField - Field to get
// Output: uint64 (0 if no data)
//-----------------------------------------------------------------------------
uint64 CRecordBase::GetUint64( int iField )
{
uint8 * pubData = NULL;
uint32 cubField = 0;
DbgVerify( BGetField( iField, &pubData, &cubField ) );
Assert( 0 < cubField );
if ( NULL != pubData )
return *( uint64 * ) pubData;
else
return 0;
}
const char * CRecordBase::ReadVarCharField( const CVarCharField &field ) const
{
Assert( false );
return NULL;
}
const uint8 * CRecordBase::ReadVarDataField( const CVarField &field, uint32 *pcubField ) const
{
Assert( false );
return NULL;
}
// These may cause a realloc
bool CRecordBase::SetVarCharField( CVarCharField &field, const char *pchString, bool bTruncate, int32 iField )
{
Assert( false );
return false ;
}
void CRecordBase::SetVarDataField( CVarField &field, const void *pvData, uint32 cubData )
{
Assert( false );
}
//-----------------------------------------------------------------------------
// Purpose: Read data from a varchar field
// Input: field - opaque field object to read from
// Output: pointer to data - may be NULL if that field is empty.
//-----------------------------------------------------------------------------
const char * CRecordVar::ReadVarCharField( const CVarCharField &field ) const
{
Assert ( GetPSchema()->BHasVariableFields() );
uint8 *pubData;
uint32 cubData;
if ( GetPSchema()->BGetVarField( PubRecordFixed(), &field, &pubData, &cubData ) )
return (const char *)pubData;
else
return "";
}
//-----------------------------------------------------------------------------
// Purpose: Read data from a vardata field
// Input: field - opaque field object to read from
// Output: pointer to data - may be NULL if that field is empty.
//-----------------------------------------------------------------------------
const uint8 *CRecordVar::ReadVarDataField( const CVarField &field, uint32 *pcubField ) const
{
Assert ( GetPSchema()->BHasVariableFields() );
uint8 *pubData;
*pcubField = 0;
if ( GetPSchema()->BGetVarField( PubRecordFixed(), &field, &pubData, pcubField ) )
return pubData;
else
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Update (in memory) a varchar field
// Input: field - opaque field object to update
// pchString - string data to set
//-----------------------------------------------------------------------------
bool CRecordVar::SetVarCharField( CVarCharField &field, const char *pchString, bool bTruncate, int32 iField )
{
Assert ( GetPSchema()->BHasVariableFields() );
if( iField < 0 )
{
AssertMsg1( false, "Encountered a bad call to SetVarCharField with an invalid field specified: %d", iField );
return false;
}
bool bTruncated = false;
int cchLen = Q_strlen( pchString ) + 1;
// since we're a VARCHAR field, cbMaxLength is the length in characters
const int cchMaxLength = m_pSchema->GetField( iField ).m_cchMaxLength;
if ( ( cchMaxLength > 0 ) && ( cchLen > cchMaxLength ) )
{
if( bTruncate )
{
bTruncated = true;
cchLen = cchMaxLength;
}
else
{
// caller should check his data and not pass stuff that wont fit
AssertMsg4( false, "Overflow in SetVarCharField (%u > %u) for column %s in table %s", cchLen, cchMaxLength, m_pSchema->GetField( iField ).m_rgchName, m_pSchema->GetPchName() );
return false;
}
}
bool bRealloced = false;
bool fSuccess = GetPSchema()->BSetVarField( PubRecordFixed(), &field, pchString, cchLen, &bRealloced, BFlagSet( k_EAllocatedVarBlock ) );
if( fSuccess && bTruncated )
{
//make sure the last character is NULL if we truncated
VarFieldBlockInfo_t *pBlock = GetPSchema()->PVarFieldBlockInfoFromRecord( PubRecordFixed() );
uint8 *pubVarBlock = pBlock->m_pubBlock;
char* pField = ( char* )( pubVarBlock + field.m_dubOffset );
pField[ cchLen - 1 ] = '\0';
}
if ( bRealloced )
SetFlag( k_EAllocatedVarBlock, true );
return fSuccess;
}
//-----------------------------------------------------------------------------
// Purpose: Update (in memory) a vardata field
// Input: field - opaque field object to update
// pvData - pointer to data to put there
// cubData - size in bytes of the data
//-----------------------------------------------------------------------------
void CRecordVar::SetVarDataField( CVarField &field, const void *pvData, uint32 cubData )
{
Assert ( GetPSchema()->BHasVariableFields() );
bool bRealloced = false;
GetPSchema()->BSetVarField( PubRecordFixed(), &field, pvData, cubData, &bRealloced, BFlagSet( k_EAllocatedVarBlock ) );
if ( bRealloced )
SetFlag( k_EAllocatedVarBlock, true );
}
//-----------------------------------------------------------------------------
// Purpose: Set or clear the specified flag in m_nFlags
// Input: eFlag - flag (single bit) to change
// bSet - Set it, else clear it
//-----------------------------------------------------------------------------
void CRecordVar::SetFlag( int eFlag, bool bSet )
{
if ( bSet )
m_nFlags |= eFlag;
else
m_nFlags &= ~eFlag;
}
//-----------------------------------------------------------------------------
// Purpose: Get the state of the specified flag
// Input: eFlag - flag (single bit) to check
//-----------------------------------------------------------------------------
bool CRecordVar::BFlagSet( int eFlag ) const
{
return 0 != ( m_nFlags & eFlag );
}
#ifdef DBGFLAG_VALIDATE
//-----------------------------------------------------------------------------
// Purpose: Run a global validation pass on all of our data structures and memory
// allocations.
// Input: validator - Our global validator object
// pchName - Our name (typically a member var in our container)
//-----------------------------------------------------------------------------
void CRecordBase::Validate( CValidator &validator, const char *pchName )
{
VALIDATE_SCOPE();
}
void CRecordVar::Validate( CValidator &validator, const char *pchName )
{
VALIDATE_SCOPE();
if ( BFlagSet( k_EAllocatedVarBlock ) )
{
validator.ClaimMemory( GetPSchema()->PVarFieldBlockInfoFromRecord( PubRecordFixed() )->m_pubBlock );
}
}
void CRecordExternal::Validate( CValidator &validator, const char *pchName )
{
if ( BFlagSet( k_EAllocatedFixed ) )
{
validator.ClaimMemory( m_pubRecordFixedExternal );
}
CRecordBase::Validate( validator, pchName );
}
void CRecordBase::ValidateStatics( CValidator &validator, const char *pchName )
{
VALIDATE_SCOPE_STATIC( "CRecordBase class statics" );
}
#endif // DBGFLAG_VALIDATE
//-----------------------------------------------------------------------------
// Purpose: Return the schema for this record type
//-----------------------------------------------------------------------------
CSchema *CRecordType::GetSchema() const
{
return &GSchemaFull().GetSchema( GetITable() );
}
//-----------------------------------------------------------------------------
// Purpose: Return the CRecordInfo for this record type
//-----------------------------------------------------------------------------
CRecordInfo *CRecordType::GetRecordInfo() const
{
return GetSchema()->GetRecordInfo();
}
} // namespace GCSDK
+915
View File
@@ -0,0 +1,915 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================
#include "stdafx.h"
//#include "sqlaccess.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
namespace GCSDK
{
// Memory pool for CRecordInfo
CThreadSafeClassMemoryPool<CRecordInfo> CRecordInfo::sm_MemPoolRecordInfo( 10, UTLMEMORYPOOL_GROW_FAST );
#ifdef _DEBUG
// validation tracking
CUtlRBTree<CRecordInfo *, int > CRecordInfo::sm_mapPMemPoolRecordInfo( DefLessFunc( CRecordInfo *) );
CThreadMutex CRecordInfo::sm_mutexMemPoolRecordInfo;
#endif
//-----------------------------------------------------------------------------
// determine if this fieldset is equal to the other one
//-----------------------------------------------------------------------------
/* static */
bool FieldSet_t::CompareFieldSets( const FieldSet_t& refThis, CRecordInfo* pRecordInfoThis,
const FieldSet_t& refOther, CRecordInfo* pRecordInfoOther )
{
// same number of columns?
int cColumns = refThis.GetCount();
if ( refOther.GetCount() != cColumns )
return false;
int cIncludedColumns = refThis.GetIncludedCount();
if ( refOther.GetIncludedCount() != cIncludedColumns )
return false;
// do the regular columns first; this is order-dependent
for ( int m = 0; m < cColumns; m++ )
{
int nThisField = refThis.GetField( m );
const CColumnInfo& refThisColumn = pRecordInfoThis->GetColumnInfo( nThisField );
int nOtherField = refOther.GetField( m );
const CColumnInfo& refOtherColumn = pRecordInfoOther->GetColumnInfo( nOtherField );
if ( refOtherColumn != refThisColumn )
{
return false;
}
}
// do the included columns now; order independent
for ( int m = 0; m < cIncludedColumns; m++ )
{
int nThisField = refThis.GetIncludedField( m );
const CColumnInfo& refThisColumn = pRecordInfoThis->GetColumnInfo( nThisField );
bool bFoundMatch = false;
for ( int n = 0; n < cIncludedColumns; n++ )
{
int nOtherField = refOther.GetIncludedField( n );
const CColumnInfo& refOtherColumn = pRecordInfoOther->GetColumnInfo( nOtherField );
if ( refOtherColumn == refThisColumn )
{
bFoundMatch = true;
break;
}
}
if ( !bFoundMatch )
return false;
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CRecordInfo::CRecordInfo()
: m_MapIColumnInfo( 0, 0, CaselessStringLessThan )
{
m_rgchName[0] = 0;
m_bPreparedForUse = false;
m_bAllColumnsAdded = false;
m_bHaveChecksum = false;
m_bHaveColumnNameIndex = false;
m_nHasPrimaryKey = k_EPrimaryKeyTypeNone;
m_iPKIndex = -1;
m_cubFixedSize = 0;
m_nChecksum = 0;
m_eSchemaCatalog = k_ESchemaCatalogInvalid;
m_nTableID = 0;
}
//-----------------------------------------------------------------------------
// Purpose: Initializes this record info from DS equivalent information
//-----------------------------------------------------------------------------
void CRecordInfo::InitFromDSSchema( CSchema *pSchema )
{
// copy the name over
SetName( pSchema->GetPchName() );
// copy each of the fields, preallocating capacity
int cFields = pSchema->GetCField();
m_VecColumnInfo.EnsureCapacity( cFields );
for ( int iField = 0; iField < cFields; iField++ )
{
Field_t &field = pSchema->GetField( iField );
AddColumn( field.m_rgchSQLName, iField+1, field.m_EType, field.m_cubLength, field.m_nColFlags, field.m_cchMaxLength );
}
m_nTableID = pSchema->GetITable();
// copy the list of PK index fields
m_iPKIndex = pSchema->GetPKIndex( );
// copy the list of Indexes
m_VecIndexes = pSchema->GetIndexes( );
// which schema?
m_eSchemaCatalog = pSchema->GetESchemaCatalog();
// copy full-text column list
// and the index of the catalog it will create on
m_vecFTSFields = pSchema->GetFTSColumns();
m_nFullTextCatalogIndex = pSchema->GetFTSIndexCatalog();
// Copy over the FK data
int cFKs = pSchema->GetFKCount();
for ( int i = 0; i < cFKs; ++i )
{
FKData_t &fkData = pSchema->GetFKData( i );
AddFK( fkData );
}
// prepare for use
PrepareForUse( );
}
//-----------------------------------------------------------------------------
// Purpose: Adds a new column to this record info
// Input: pchName - column name
// nSQLColumn - column index in SQL to bind to (1-based)
// eType - data type of column
// cubFixedSize - for fixed-size fields, the size
// nColFlags - attributes
//-----------------------------------------------------------------------------
void CRecordInfo::AddColumn( const char *pchName, int nSQLColumn, EGCSQLType eType, int cubFixedSize, int nColFlags, int cchMaxSize )
{
Assert( !m_bPreparedForUse );
if ( m_bPreparedForUse )
return;
uint32 unColumn = m_VecColumnInfo.AddToTail();
CColumnInfo &columnInfo = m_VecColumnInfo[unColumn];
columnInfo.Set( pchName, nSQLColumn, eType, cubFixedSize, nColFlags, cchMaxSize );
}
//-----------------------------------------------------------------------------
// Purpose: Adds a new FK to this record info
//-----------------------------------------------------------------------------
void CRecordInfo::AddFK( const FKData_t &fkData )
{
m_VecFKData.AddToTail( fkData );
}
//-----------------------------------------------------------------------------
// Purpose: compare function to sort by column name
//-----------------------------------------------------------------------------
int __cdecl CompareColumnInfo( const CColumnInfo *pColumnInfoLeft, const CColumnInfo *pColumnInfoRight )
{
const char *pchLeft = ( (CColumnInfo *) pColumnInfoLeft )->GetName();
const char *pchRight = ( (CColumnInfo *) pColumnInfoRight )->GetName();
Assert( pchLeft && pchLeft[0] );
Assert( pchRight && pchRight[0] );
return Q_stricmp( pchLeft, pchRight );
}
//-----------------------------------------------------------------------------
// Purpose: compares this record info to another record info
//-----------------------------------------------------------------------------
bool CRecordInfo::EqualTo( CRecordInfo* pOther )
{
int nOurs = GetChecksum();
int nTheirs = pOther->GetChecksum();
// if this much isn't equal, we're no good
if (nOurs != nTheirs)
return false;
if ( !CompareIndexLists( pOther ) )
return false;
if ( !CompareFKs( pOther ) )
return false;
return CompareFTSIndexLists( pOther );
}
//-----------------------------------------------------------------------------
// Purpose: format the index list into a string
//-----------------------------------------------------------------------------
void CRecordInfo::GetIndexFieldList( CFmtStr1024 *pstr, int nIndents ) const
{
// table name at first
pstr->sprintf( "Table %s:\n", this->GetName() );
// for each of the indexes ...
for ( int n = 0; n < m_VecIndexes.Count(); n++ )
{
const FieldSet_t& fs = m_VecIndexes[n];
// indent enough
for ( int x = 0; x < nIndents; x++ )
{
pstr->Append( "\t" );
}
// show if it is clustered or not
pstr->AppendFormat( "Index %d (%s): %sclustered, %sunique {", n,
fs.GetIndexName(),
fs.IsClustered() ? "" : "non-",
fs.IsUnique() ? "" : "non-" );
// then show all the columns
for (int m = 0; m < fs.GetCount(); m++ )
{
int x = fs.GetField( m );
const char* pstrName = m_VecColumnInfo[x].GetName();
pstr->AppendFormat( "%s %s", ( m == 0 ) ? "" : ",", pstrName );
}
// then the included columns, too
for ( int m = 0; m < fs.GetIncludedCount(); m++ )
{
int x = fs.GetIncludedField( m );
const char* pstrName = m_VecColumnInfo[x].GetName();
pstr->AppendFormat( ", *%s", pstrName );
}
pstr->Append( " }\n" );
}
return;
}
//-----------------------------------------------------------------------------
// Purpose: Get the number of foreign key constraints defined for the table
//-----------------------------------------------------------------------------
int CRecordInfo::GetFKCount()
{
return m_VecFKData.Count();
}
//-----------------------------------------------------------------------------
// Purpose: Get data for a foreign key by index (valid for 0...GetFKCount()-1)
//-----------------------------------------------------------------------------
FKData_t &CRecordInfo::GetFKData( int iIndex )
{
return m_VecFKData[iIndex];
}
//-----------------------------------------------------------------------------
// Purpose: format the FK list into a string
//-----------------------------------------------------------------------------
void CRecordInfo::GetFKListString( CFmtStr1024 *pstr, int nIndents )
{
// table name at first
pstr->sprintf( "Table %s Foreign Keys: \n", this->GetName() );
if ( m_VecFKData.Count() == 0 )
{
// indent enough
pstr->AppendIndent( nIndents );
pstr->Append( "No foreign keys for table\n" );
}
else
{
for ( int n = 0; n < m_VecFKData.Count(); n++ )
{
// indent enough
pstr->AppendIndent( nIndents );
FKData_t &fkData = m_VecFKData[n];
CFmtStr sColumns, sParentColumns;
FOR_EACH_VEC( fkData.m_VecColumnRelations, i )
{
FKColumnRelation_t &colRelation = fkData.m_VecColumnRelations[i];
if ( i > 0)
{
sColumns += ",";
sParentColumns += ",";
}
sColumns += colRelation.m_rgchCol;
sParentColumns += colRelation.m_rgchParentCol;
}
pstr->AppendFormat( "CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s(%s) ON DELETE %s ON UPDATE %s\n",
fkData.m_rgchName, sColumns.Access(), fkData.m_rgchParentTableName, sParentColumns.Access(),
PchNameFromEForeignKeyAction( fkData.m_eOnDeleteAction ), PchNameFromEForeignKeyAction( fkData.m_eOnUpdateAction ) );
}
}
return;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CRecordInfo::AddFTSFields( CUtlVector< int > &vecFields )
{
AssertMsg( m_vecFTSFields.Count() == 0, "Only one FTS index per table" );
FOR_EACH_VEC( vecFields, n )
{
int nField = vecFields[n];
m_vecFTSFields.AddToTail( nField );
}
return;
}
//-----------------------------------------------------------------------------
// Purpose: compares FK lists in this record with those of another
//-----------------------------------------------------------------------------
bool CRecordInfo::CompareFKs( CRecordInfo *pOther )
{
if ( pOther->m_VecFKData.Count() != m_VecFKData.Count() )
return false;
for( int i=0; i < m_VecFKData.Count(); ++i )
{
FKData_t &fkDataMine = m_VecFKData[i];
bool bFoundInOther = false;
for ( int j=0; j < pOther->m_VecFKData.Count(); ++j )
{
FKData_t &fkDataOther = pOther->m_VecFKData[j];
if ( fkDataMine == fkDataOther )
{
bFoundInOther = true;
break;
}
}
if ( !bFoundInOther )
return false;
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Locate an index by its properties (ignoring the name).
// Returns position of index in the index array, or -1 if not found.
//-----------------------------------------------------------------------------
int CRecordInfo::FindIndex( CRecordInfo *pRec, const FieldSet_t& fieldSet )
{
for ( int i = 0; i < m_VecIndexes.Count(); i++ )
{
if ( FieldSet_t::CompareFieldSets( m_VecIndexes[i], this, fieldSet, pRec ) )
return i;
}
// Not found
return -1;
}
//-----------------------------------------------------------------------------
// Purpose: Locate an index with the given name.
// Returns position of index in the index array, or -1 if not found.
//-----------------------------------------------------------------------------
int CRecordInfo::FindIndexByName( const char *pszName ) const
{
for ( int i = 0; i < m_VecIndexes.Count(); i++ )
{
if ( V_stricmp( m_VecIndexes[i].GetIndexName(), pszName )== 0 )
return i;
}
// Not found
return -1;
}
//-----------------------------------------------------------------------------
// Purpose: compares index lists in this record with those of another
//-----------------------------------------------------------------------------
bool CRecordInfo::CompareIndexLists( CRecordInfo* pOther )
{
// compare the index lists (but don't use CRCs)
// different size? can't be the same
if ( pOther->GetIndexFieldCount() != GetIndexFieldCount() )
{
return false;
}
// We have to loop through both lists of indexes and try to find a match.
// We also must make sure the match is exact, and that no previous match
// can alias another attempt at a match. Pretty messy, but with no available
// identity over index objects, we're forced to a suboptimal solution.
int nIndexes = GetIndexFieldCount();
// get a copy of the other index vector, which we'll remove items from as
// matches are found.
CUtlVector<FieldSet_t> vecOtherIndexes;
vecOtherIndexes.CopyArray( pOther->GetIndexFields().Base(), nIndexes );
for ( int nOurs = 0; nOurs < nIndexes; nOurs++ )
{
int nOtherMatchIndex = -1;
const FieldSet_t& refOurs = GetIndexFields()[nOurs];
// rip through copy of other to find one that matches
for ( int nOther = 0; nOther < vecOtherIndexes.Count(); nOther++ )
{
const FieldSet_t& refOther = vecOtherIndexes[nOther];
if ( FieldSet_t::CompareFieldSets( refOurs, this, refOther, pOther ) )
{
nOtherMatchIndex = nOther;
break;
}
}
if ( nOtherMatchIndex >= 0 )
{
// this works! remove it from other copy
vecOtherIndexes.Remove( nOtherMatchIndex );
}
else
{
// something didn't match, so bail out early
return false;
}
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose: compares full-text indexes for this record with those of another
// column order in an FTS is irrelevant, so this is a simple match
//-----------------------------------------------------------------------------
bool CRecordInfo::CompareFTSIndexLists( CRecordInfo* pOther ) const
{
// compare full-text index columns
if ( m_vecFTSFields.Count() != pOther->m_vecFTSFields.Count() )
{
// counts don't match, so obviously no good
return false;
}
for ( int nColumnIndex = 0; nColumnIndex < m_vecFTSFields.Count(); nColumnIndex++ )
{
bool bFound = false;
for ( int nInnerIndex = 0; nInnerIndex < pOther->m_vecFTSFields.Count(); nInnerIndex++ )
{
if ( m_vecFTSFields[nInnerIndex] == pOther->m_vecFTSFields[nColumnIndex] )
{
bFound = true;
break;
}
}
if ( !bFound )
return false;
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Returns the checksum for this record info
//-----------------------------------------------------------------------------
int CRecordInfo::GetChecksum()
{
Assert( m_bPreparedForUse );
// calculate it now if we haven't already
if ( !m_bHaveChecksum )
CalculateChecksum();
return m_nChecksum;
}
//-----------------------------------------------------------------------------
// Purpose: Prepares this object for use after all columns have been added
//-----------------------------------------------------------------------------
void CRecordInfo::PrepareForUse()
{
Assert( !m_bPreparedForUse );
Assert( 0 == m_cubFixedSize );
Assert( 0 == m_nChecksum );
SetAllColumnsAdded();
FOR_EACH_VEC( m_VecColumnInfo, nColumn )
{
CColumnInfo &columnInfo = m_VecColumnInfo[nColumn];
// keep track of total fixed size of all columns
if ( !columnInfo.BIsVariableLength() )
m_cubFixedSize += columnInfo.GetFixedSize();
if ( columnInfo.BIsPrimaryKey() )
{
// a PK column! if we have seen one before,
// know we have a-column PK; otherwise, a single column PK
if (m_nHasPrimaryKey == k_EPrimaryKeyTypeNone)
m_nHasPrimaryKey = k_EPrimaryKeyTypeSingle;
else
m_nHasPrimaryKey = k_EPrimaryKeyTypeMulti;
}
}
// make sure count matches the enum
/*
Assert( ( m_nHasPrimaryKey == k_EPrimaryKeyTypeNone && m_VecPKFields.Count() == 0 ) ||
( m_nHasPrimaryKey == k_EPrimaryKeyTypeMulti && m_VecPKFields.Count() > 1) ||
( m_nHasPrimaryKey == k_EPrimaryKeyTypeSingle && m_VecPKFields.Count() == 1) );
*/
m_bPreparedForUse = true;
}
//-----------------------------------------------------------------------------
// Purpose: Returns index of column with specified name
// Input: pchName - column name
// punColumn - pointer to fill in with index
// Output: return true if found, false otherwise
//-----------------------------------------------------------------------------
bool CRecordInfo::BFindColumnByName( const char *pchName, int *punColumn )
{
Assert( m_bAllColumnsAdded );
Assert( pchName && *pchName );
Assert( punColumn );
*punColumn = -1;
// if we haven't already built the name index, build it now
if ( !m_bHaveColumnNameIndex )
BuildColumnNameIndex();
*punColumn = m_MapIColumnInfo.Find( pchName );
return ( m_MapIColumnInfo.InvalidIndex() != *punColumn );
}
//-----------------------------------------------------------------------------
// Purpose: Sets the name of this record info
// Input: pchName - name
// Notes: record info that describes a table will have a name (the table name);
// record info that describes a result set will not
//-----------------------------------------------------------------------------
void CRecordInfo::SetName( const char *pchName )
{
Assert( pchName && *pchName );
Assert( !m_bPreparedForUse ); // don't change this after prepared for use
Q_strncpy( m_rgchName, pchName, Q_ARRAYSIZE( m_rgchName ) );
}
//-----------------------------------------------------------------------------
// Purpose: Builds the column name index for fast lookup by name
//-----------------------------------------------------------------------------
void CRecordInfo::BuildColumnNameIndex()
{
AUTO_LOCK( m_Mutex );
if ( m_bHaveColumnNameIndex )
return;
Assert( m_bAllColumnsAdded );
Assert( 0 == m_MapIColumnInfo.Count() );
FOR_EACH_VEC( m_VecColumnInfo, nColumn )
{
// build name->column index map
CColumnInfo &columnInfo = m_VecColumnInfo[nColumn];
m_MapIColumnInfo.Insert( columnInfo.GetName(), nColumn );
}
m_bHaveColumnNameIndex = true;
}
//-----------------------------------------------------------------------------
// Purpose: Calculates the checksum for this record info
//-----------------------------------------------------------------------------
void CRecordInfo::CalculateChecksum()
{
AUTO_LOCK( m_Mutex );
if ( m_bHaveChecksum )
return;
// build the column name index if necessary
if ( !m_bHaveColumnNameIndex )
BuildColumnNameIndex();
CRC32_t crc32;
CRC32_Init( &crc32 );
FOR_EACH_MAP( m_MapIColumnInfo, iMapItem )
{
uint32 unColumn = m_MapIColumnInfo[iMapItem];
CColumnInfo &columnInfo = m_VecColumnInfo[unColumn];
// calculate checksum of all of our columns
columnInfo.CalculateChecksum();
int nChecksum = columnInfo.GetChecksum();
CRC32_ProcessBuffer( &crc32, (void*) &nChecksum, sizeof( nChecksum ) );
}
// keep checksum for entire record info
CRC32_Final( &crc32 );
m_nChecksum = crc32;
m_bHaveChecksum = true;
}
//-----------------------------------------------------------------------------
// Purpose: add another index disallowing duplicates. If a duplicate item is
// found, we'll set the flags on the new item from the existing one.
//-----------------------------------------------------------------------------
int CRecordInfo::AddIndex( const FieldSet_t& fieldSet )
{
for ( int n = 0; n < m_VecIndexes.Count(); n++ )
{
FieldSet_t& fs = m_VecIndexes[n];
if ( FieldSet_t::CompareFieldSets( fieldSet, this, fs, this ) )
{
fs.SetClustered( fs.IsClustered() );
return -1;
}
}
int nRet = m_VecIndexes.AddToTail( fieldSet );
return nRet;
}
//-----------------------------------------------------------------------------
// Purpose: Returns true if there is an IDENTITY column in the record info
//-----------------------------------------------------------------------------
bool CRecordInfo::BHasIdentity() const
{
FOR_EACH_VEC( m_VecColumnInfo, nColumn)
{
if( m_VecColumnInfo[nColumn].BIsAutoIncrement() )
return true;
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CColumnInfo::CColumnInfo()
{
m_rgchName[0] = 0;
m_nSQLColumn = 0;
m_eType = k_EGCSQLTypeInvalid;
m_nColFlags = 0;
m_cubFixedSize = 0;
m_cchMaxSize = 0;
m_nChecksum = 0;
m_bHaveChecksum = false;
}
//-----------------------------------------------------------------------------
// Purpose: Sets column info for this column
// Input: pchName - column name
// nSQLColumn - column index in SQL to bind to (1-based)
// eType - data type of column
// cubFixedSize - for fixed-size fields, the size
// nColFlags - attributes
//-----------------------------------------------------------------------------
void CColumnInfo::Set( const char *pchName, int nSQLColumn, EGCSQLType eType, int cubFixedSize, int nColFlags, int cchMaxSize )
{
Assert( !m_rgchName[0] );
Q_strncpy( m_rgchName, pchName, Q_ARRAYSIZE( m_rgchName ) );
m_nSQLColumn = nSQLColumn;
m_eType = eType;
m_nColFlags = nColFlags;
ValidateColFlags();
if ( !BIsVariableLength() )
{
Assert( cubFixedSize > 0 );
m_cubFixedSize = cubFixedSize;
m_cchMaxSize = 0;
}
else
{
// it's variable length, so we need a max length
m_cchMaxSize = cchMaxSize;
m_cubFixedSize = 0;
}
}
//-----------------------------------------------------------------------------
// Purpose: returns whether this column is variable length
//-----------------------------------------------------------------------------
bool CColumnInfo::BIsVariableLength() const
{
return m_eType == k_EGCSQLType_Blob || m_eType == k_EGCSQLType_String || m_eType == k_EGCSQLType_Image;
}
//-----------------------------------------------------------------------------
// Purpose: convert column flags to a visible representation
//-----------------------------------------------------------------------------
void CColumnInfo::GetColFlagDescription( char* pstrOut, int cubOutLength ) const
{
if ( m_nColFlags == 0 )
Q_strncpy( pstrOut, "(none)", cubOutLength );
else
{
pstrOut[0] = 0;
if ( m_nColFlags & k_nColFlagIndexed )
Q_strncat( pstrOut, "(Indexed)", cubOutLength );
if ( m_nColFlags & k_nColFlagUnique )
Q_strncat( pstrOut, "(Unique)", cubOutLength );
if ( m_nColFlags & k_nColFlagPrimaryKey )
Q_strncat( pstrOut, "(PrimaryKey)", cubOutLength );
if ( m_nColFlags & k_nColFlagAutoIncrement )
Q_strncat( pstrOut, "(AutoIncrement)", cubOutLength );
if ( m_nColFlags & k_nColFlagClustered )
Q_strncat( pstrOut, "(Clustered)", cubOutLength );
}
return;
}
//-----------------------------------------------------------------------------
// Purpose: sets column flag bits
// Input: nColFlag - bits to set. (Other bits are not cleared.)
//-----------------------------------------------------------------------------
void CColumnInfo::SetColFlagBits( int nColFlag )
{
ValidateColFlags();
m_nColFlags |= nColFlag; // set these bits
ValidateColFlags();
}
//-----------------------------------------------------------------------------
// Purpose: Calculates the checksum for this column
//-----------------------------------------------------------------------------
void CColumnInfo::CalculateChecksum()
{
if ( m_bHaveChecksum )
return;
// calculate checksum of this column for easy comparsion
CRC32_t crc32;
CRC32_Init( &crc32 );
CRC32_ProcessBuffer( &crc32, (void*) m_rgchName, Q_strlen( m_rgchName ) );
CRC32_ProcessBuffer( &crc32, (void*) &m_nColFlags, sizeof( m_nColFlags ) );
CRC32_ProcessBuffer( &crc32, (void*) &m_eType, sizeof( m_eType ) );
CRC32_ProcessBuffer( &crc32, (void*) &m_cubFixedSize, sizeof( m_cubFixedSize ) );
CRC32_ProcessBuffer( &crc32, (void*) &m_cchMaxSize, sizeof( m_cchMaxSize ) );
CRC32_Final( &crc32 );
m_nChecksum = crc32;
m_bHaveChecksum = true;
}
//-----------------------------------------------------------------------------
// determine if this CColumnInfo is the same as the referenced
//-----------------------------------------------------------------------------
bool CColumnInfo::operator==( const CColumnInfo& refOther ) const
{
if ( m_eType != refOther.m_eType )
return false;
if ( m_cubFixedSize != refOther.m_cubFixedSize )
return false;
if ( m_cchMaxSize != refOther.m_cchMaxSize )
return false;
if ( m_nColFlags != refOther.m_nColFlags )
return false;
if ( 0 != Q_strcmp( m_rgchName, refOther.m_rgchName ) )
return false;
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Validates that column flags are set in valid combinations
//-----------------------------------------------------------------------------
void CColumnInfo::ValidateColFlags() const
{
// Check that column flags follow rules about how columns get expressed in SQL
if ( m_nColFlags & k_nColFlagPrimaryKey )
{
// a primary key must also be unique and indexed
Assert( m_nColFlags & k_nColFlagUnique );
Assert( m_nColFlags & k_nColFlagIndexed );
}
// a column with uniqueness constraint must also be indexed
if ( m_nColFlags & k_nColFlagUnique )
Assert( m_nColFlags & k_nColFlagIndexed );
}
CRecordInfo *CRecordInfo::Alloc()
{
CRecordInfo *pRecordInfo = sm_MemPoolRecordInfo.Alloc();
#ifdef _DEBUG
AUTO_LOCK( sm_mutexMemPoolRecordInfo );
sm_mapPMemPoolRecordInfo.Insert( pRecordInfo );
#endif
return pRecordInfo;
}
void CRecordInfo::DestroyThis()
{
#ifdef _DEBUG
AUTO_LOCK( sm_mutexMemPoolRecordInfo );
sm_mapPMemPoolRecordInfo.Remove( this );
#endif
sm_MemPoolRecordInfo.Free( this );
}
#ifdef DBGFLAG_VALIDATE
void CRecordInfo::ValidateStatics( CValidator &validator, const char *pchName )
{
VALIDATE_SCOPE_STATIC( "CRecordInfo class statics" );
ValidateObj( sm_MemPoolRecordInfo );
#ifdef _DEBUG
AUTO_LOCK( sm_mutexMemPoolRecordInfo );
ValidateObj( sm_mapPMemPoolRecordInfo );
FOR_EACH_MAP_FAST( sm_mapPMemPoolRecordInfo, i )
{
sm_mapPMemPoolRecordInfo[i]->Validate( validator, "sm_mapPMemPoolRecordInfo[i]" );
}
#endif
}
void CRecordInfo::Validate( CValidator &validator, const char *pchName )
{
VALIDATE_SCOPE();
m_VecIndexes.Validate( validator, "m_VecIndexes" );
ValidateObj( m_VecFKData );
FOR_EACH_VEC( m_VecFKData, i )
{
ValidateObj( m_VecFKData[i] );
}
for ( int iIndex = 0; iIndex < m_VecIndexes.Count(); iIndex++ )
{
ValidateObj( m_VecIndexes[iIndex] );
}
ValidateObj( m_vecFTSFields );
ValidateObj( m_VecColumnInfo );
FOR_EACH_VEC( m_VecColumnInfo, nColumn )
{
CColumnInfo &columnInfo = GetColumnInfo( nColumn );
ValidateObj( columnInfo );
}
ValidateObj( m_MapIColumnInfo );
}
void CColumnInfo::Validate( CValidator &validator, const char *pchName )
{
VALIDATE_SCOPE();
}
#endif // DBGFLAG_VALIDATE
} // namespace GCSDK
File diff suppressed because it is too large Load Diff
+409
View File
@@ -0,0 +1,409 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================
#include "stdafx.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
namespace GCSDK
{
CSchemaFull g_SchemaFull;
CSchemaFull & GSchemaFull()
{
return g_SchemaFull;
}
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CSchemaFull::CSchemaFull()
{
m_pubScratchBuffer = NULL;
m_cubScratchBuffer = 0;
m_unCheckSum = 0;
m_mapFTSEnabled.SetLessFunc( DefLessFunc( enum ESchemaCatalog ) );
}
//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
CSchemaFull::~CSchemaFull()
{
Uninit();
}
//-----------------------------------------------------------------------------
// Purpose: Call this after you've finished setting up the SchemaFull (either
// by loading it or by in GenerateIntrinsic). It calculates our checksum
// and allocates our scratch buffer.
//-----------------------------------------------------------------------------
void CSchemaFull::FinishInit()
{
// Calculate our checksum
m_unCheckSum = 0;
for ( int iSchema = 0; iSchema < m_VecSchema.Count(); iSchema++ )
m_unCheckSum += m_VecSchema[iSchema].CalcChecksum();
// Allocate our scratch buffer
Assert( NULL == m_pubScratchBuffer );
// Include some slop for field IDs and sizes in a sparse record
// 2k is way overkill but still no big deal
m_cubScratchBuffer = k_cubRecordMax + 2048;
m_pubScratchBuffer = ( uint8 * ) malloc( m_cubScratchBuffer );
}
//-----------------------------------------------------------------------------
// Purpose: Call this after you've finished setting up the SchemaFull (either
// by loading it or by in GenerateIntrinsic). It calculates our checksum
// and allocates our scratch buffer.
//-----------------------------------------------------------------------------
void CSchemaFull::SetITable( CSchema* pSchema, int iTable )
{
// make sure we don't have this schema anywhere already
for ( int iSchema = 0; iSchema < m_VecSchema.Count(); iSchema++ )
{
if ( pSchema != &m_VecSchema[iSchema] )
AssertFatalMsg( m_VecSchema[iSchema].GetITable() != iTable, "Duplicate iTable in schema definition.\n" );
}
// set the pSchema object
pSchema->SetITable( iTable );
}
//-----------------------------------------------------------------------------
// Purpose: Uninits the schema. Need to call this explicitly before app shutdown
// on static instances of this object, as the CSchema objects
// point to memory in static memory pools which may destruct
// before static instances of this object.
//-----------------------------------------------------------------------------
void CSchemaFull::Uninit()
{
m_VecSchema.RemoveAll();
if ( NULL != m_pubScratchBuffer )
{
free( m_pubScratchBuffer );
m_pubScratchBuffer = NULL;
}
}
//-----------------------------------------------------------------------------
// Purpose: Get the scratch buffer. It is large enough to handle any
// record, sparse or otherwise
//
//-----------------------------------------------------------------------------
uint8* CSchemaFull::GetPubScratchBuffer( )
{
return m_pubScratchBuffer;
}
//-----------------------------------------------------------------------------
// Purpose: This is used during the generation of our intrinsic schema. We've
// added a new schema to ourselves, and we need to make sure that it
// matches the corresponding C class.
// Input: pSchema - Schema to check
// cField - Number of fields the schema should contain.
// cubRecord - Size of a record in the schema
//-----------------------------------------------------------------------------
void CSchemaFull::CheckSchema( CSchema *pSchema, int cField, uint32 cubRecord )
{
// We generate our structures and our schema using macros that operate on the
// same source. We check a couple of things to make sure that they're properly in sync.
// This will fail if the schema's definition specifies the wrong iTable
if ( pSchema != &m_VecSchema[pSchema->GetITable()] )
{
EmitError( SPEW_SQL, "Table %s has a bad iTable\n", pSchema->GetPchName() );
}
// This will fail if there are missing lines in the schema definition
if ( pSchema->GetCField() != cField )
{
EmitError( SPEW_SQL, "Badly formed table %s (blank line in schema def?)\n", pSchema->GetPchName() );
AssertFatal( false );
}
// This is unlikely to fail. It indicates some kind of size mismatch (maybe a packing problem?)
if ( pSchema->CubRecordFixed() != cubRecord )
{
// You may hit this if END_FIELDDATA_HAS_VAR_FIELDS is not used properly
EmitError( SPEW_SQL, "Table %s has an inconsistent size (class = %d, schema = %d)\n",
pSchema->GetPchName(), cubRecord, pSchema->CubRecordFixed() );
AssertFatal( false );
}
}
//-----------------------------------------------------------------------------
// Purpose: Finds the table with a given name.
// Input: pchName - Name of the table to search for
// Output: Index of the matching table ( k_iTableNil if there isn't one)
//-----------------------------------------------------------------------------
int CSchemaFull::FindITable( const char *pchName )
{
for ( int iSchema = 0; iSchema < m_VecSchema.Count(); iSchema++ )
{
if ( 0 == Q_strcmp( pchName, m_VecSchema[iSchema].GetPchName() ) )
return iSchema;
}
return k_iTableNil;
}
//-----------------------------------------------------------------------------
// Purpose: Finds the table with a given iTable (iSchema)
// Input: iTable -
// Output: NULL or a const char * to the name (for temporary use only)
//-----------------------------------------------------------------------------
const char * CSchemaFull::PchTableFromITable( int iTable )
{
if ( iTable < 0 || iTable >= m_VecSchema.Count() )
return NULL;
else
return m_VecSchema[ iTable ].GetPchName();
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CSchemaFull::AddFullTextCatalog( enum ESchemaCatalog eCatalog, const char *pstrCatalogName, int nFileGroup )
{
CFTSCatalogInfo info;
info.m_eCatalog = eCatalog;
info.m_nFileGroup = nFileGroup;
info.m_pstrName = strdup(pstrCatalogName);
m_vecFTSCatalogs.AddToTail( info );
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
int CSchemaFull::GetFTSCatalogByName( enum ESchemaCatalog eCatalog, const char *pstrCatalogName )
{
int nIndex = -1;
FOR_EACH_VEC( m_vecFTSCatalogs, i )
{
CFTSCatalogInfo &refInfo = m_vecFTSCatalogs[ i ];
if ( 0 == Q_stricmp( pstrCatalogName, refInfo.m_pstrName ) )
{
nIndex = i;
break;
}
}
return nIndex;
}
//-----------------------------------------------------------------------------
// Purpose: turn on FTS for the named schema catalog. Called by the
// InitIntrinsic() function.
//-----------------------------------------------------------------------------
void CSchemaFull::EnableFTS( enum ESchemaCatalog eCatalog )
{
// mark it enabled in the map
m_mapFTSEnabled.Insert( eCatalog, true );
}
//-----------------------------------------------------------------------------
// Purpose: is FTS enabled for the supplied schema catalog?
//-----------------------------------------------------------------------------
bool CSchemaFull::GetFTSEnabled( enum ESchemaCatalog eCatalog )
{
int iEntry = m_mapFTSEnabled.Find( eCatalog );
if ( iEntry == m_mapFTSEnabled.InvalidIndex() )
return false;
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Adds a schema conversion instruction (for use in converting from
// a different SchemaFull to this one).
//-----------------------------------------------------------------------------
void CSchemaFull::AddDeleteTable( const char *pchTableName )
{
DeleteTable_t &deleteTable = m_VecDeleteTable[m_VecDeleteTable.AddToTail()];
Q_strncpy( deleteTable.m_rgchTableName, pchTableName, sizeof( deleteTable.m_rgchTableName ) );
}
//-----------------------------------------------------------------------------
// Purpose: Adds a schema conversion instruction (for use in converting from
// a different SchemaFull to this one).
//-----------------------------------------------------------------------------
void CSchemaFull::AddRenameTable( const char *pchTableNameOld, const char *pchTableNameNew )
{
RenameTable_t &renameTable = m_VecRenameTable[m_VecRenameTable.AddToTail()];
Q_strncpy( renameTable.m_rgchTableNameOld, pchTableNameOld, sizeof( renameTable.m_rgchTableNameOld ) );
renameTable.m_iTableDst = FindITable( pchTableNameNew );
Assert( k_iTableNil != renameTable.m_iTableDst );
}
//-----------------------------------------------------------------------------
// Purpose: Adds a schema conversion instruction (for use in converting from
// a different SchemaFull to this one).
//-----------------------------------------------------------------------------
void CSchemaFull::AddDeleteField( const char *pchTableName, const char *pchFieldName )
{
int iSchema = FindITable( pchTableName );
AssertFatal( k_iTableNil != iSchema );
m_VecSchema[iSchema].AddDeleteField( pchFieldName );
}
//-----------------------------------------------------------------------------
// Purpose: Adds a schema conversion instruction (for use in converting from
// a different SchemaFull to this one).
//-----------------------------------------------------------------------------
void CSchemaFull::AddRenameField( const char *pchTableName, const char *pchFieldNameOld, const char *pchFieldNameNew )
{
int iSchema = FindITable( pchTableName );
AssertFatal( k_iTableNil != iSchema );
m_VecSchema[iSchema].AddRenameField( pchFieldNameOld, pchFieldNameNew );
}
//-----------------------------------------------------------------------------
// Purpose: Adds a schema conversion instruction (for use in converting from
// a different SchemaFull to this one).
//-----------------------------------------------------------------------------
void CSchemaFull::AddAlterField( const char *pchTableName, const char *pchFieldNameOld, const char *pchFieldnameNew, PfnAlterField_t pfnAlterField )
{
int iSchema = FindITable( pchTableName );
AssertFatal( k_iTableNil != iSchema );
m_VecSchema[iSchema].AddAlterField( pchFieldNameOld, pchFieldnameNew, pfnAlterField );
}
//-----------------------------------------------------------------------------
// Purpose: Add a trigger to the desired schema
//-----------------------------------------------------------------------------
void CSchemaFull::AddTrigger( ESchemaCatalog eCatalog, const char *pchTableName, const char *pchTriggerName, ETriggerType eTriggerType, const char *pchTriggerText )
{
CTriggerInfo trigger;
trigger.m_eTriggerType = eTriggerType;
trigger.m_eSchemaCatalog = eCatalog;
Q_strncpy( trigger.m_szTriggerName, pchTriggerName, Q_ARRAYSIZE( trigger.m_szTriggerName ) );
Q_strncpy( trigger.m_szTriggerTableName, pchTableName, Q_ARRAYSIZE( trigger.m_szTriggerTableName ) );
trigger.m_strText = pchTriggerText;
// add it to our list
m_VecTriggers.AddToTail( trigger );
}
//-----------------------------------------------------------------------------
// Purpose: Figures out how to map a table from another SchemaFull into us.
// First we check our conversion instructions to see if any apply,
// and then we look for a straightforward match.
// Input: pchTableName - Name of the table we're trying to map
// piTableDst - [Return] Index of the table to map it to
// Output: true if we know what to do with this table (if false, the conversion
// is undefined and dangerous).
//-----------------------------------------------------------------------------
bool CSchemaFull::BCanConvertTable( const char *pchTableName, int *piTableDst )
{
// Should this table be deleted?
for ( int iDeleteTable = 0; iDeleteTable < m_VecDeleteTable.Count(); iDeleteTable++ )
{
if ( 0 == Q_strcmp( pchTableName, m_VecDeleteTable[iDeleteTable].m_rgchTableName ) )
{
*piTableDst = k_iTableNil;
return true;
}
}
// Should this table be renamed?
for ( int iRenameTable = 0; iRenameTable < m_VecRenameTable.Count(); iRenameTable++ )
{
if ( 0 == Q_strcmp( pchTableName, m_VecRenameTable[iRenameTable].m_rgchTableNameOld ) )
{
*piTableDst = m_VecRenameTable[iRenameTable].m_iTableDst;
return true;
}
}
// Find out which of our tables this table maps to (if it doesn't map
// to any of them, we don't know what to do with it).
*piTableDst = FindITable( pchTableName );
return ( k_iTableNil != *piTableDst );
}
//-----------------------------------------------------------------------------
// Purpose: Gets the default SQL schema name for a catalog
//-----------------------------------------------------------------------------
const char *CSchemaFull::GetDefaultSchemaNameForCatalog( ESchemaCatalog eCatalog )
{
// For all catalogs it's actually the same
if ( m_strDefaultSchemaName.IsEmpty() )
{
m_strDefaultSchemaName.Set( CFmtStr( "App%u", GGCBase()->GetAppID() ) );
}
return m_strDefaultSchemaName.Get();
}
#ifdef DBGFLAG_VALIDATE
//-----------------------------------------------------------------------------
// Purpose: Run a global validation pass on all of our data structures and memory
// allocations.
// Input: validator - Our global validator object
// pchName - Our name (typically a member var in our container)
//-----------------------------------------------------------------------------
void CSchemaFull::Validate( CValidator &validator, const char *pchName )
{
VALIDATE_SCOPE();
ValidateObj( m_VecSchema );
for ( int iSchema = 0; iSchema < m_VecSchema.Count(); iSchema++ )
{
ValidateObj( m_VecSchema[iSchema] );
}
ValidateObj( m_VecDeleteTable );
ValidateObj( m_VecRenameTable );
ValidateObj( m_mapFTSEnabled );
ValidateObj( m_vecFTSCatalogs );
FOR_EACH_VEC( m_vecFTSCatalogs, i )
{
ValidateObj( m_vecFTSCatalogs[i] );
}
ValidateObj( m_VecTriggers );
FOR_EACH_VEC( m_VecTriggers, i )
{
ValidateObj( m_VecTriggers[i] );
}
validator.ClaimMemory( m_pubScratchBuffer );
}
#endif // DBGFLAG_VALIDATE
} // namespace GCSDK
File diff suppressed because it is too large Load Diff
+953
View File
@@ -0,0 +1,953 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Provides access to SQL at a high level
//
//=============================================================================
#include "stdafx.h"
#include "gcsdk/sqlaccess/sqlaccess.h"
#include "gcsdk/gcsqlquery.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
template< typename LISTENER_FUNC >
static void RunAndClearListenerList( std::vector< LISTENER_FUNC > &vecListeners )
{
// Let us not underestimate the ability of random listeners to re-enter everything.
std::vector< LISTENER_FUNC > listenerCopy;
listenerCopy.swap( vecListeners );
vecListeners.clear();
// Why would you consider such a thing
DO_NOT_YIELD_THIS_SCOPE();
for ( const auto &listener : listenerCopy )
{
listener();
}
}
namespace GCSDK
{
//------------------------------------------------------------------------------------
// Purpose: Constructor
//------------------------------------------------------------------------------------
CSQLAccess::CSQLAccess( ESchemaCatalog eSchemaCatalog )
: m_eSchemaCatalog( eSchemaCatalog)
, m_pCurrentQuery( NULL )
, m_bInTransaction( false )
{
m_pQueryGroup = CGCSQLQueryGroup::Alloc();
}
//------------------------------------------------------------------------------------
// Purpose: Destructor
//------------------------------------------------------------------------------------
CSQLAccess::~CSQLAccess( )
{
SAFE_RELEASE( m_pQueryGroup );
Assert( !m_pCurrentQuery );
SAFE_DELETE( m_pCurrentQuery );
AssertMsg( !m_bInTransaction, "GCSDK::CSQLAccess object being destroyed with a transaction pending. Use BCommitTransaction or RollbackTransaction to match your BBeginTransaction call." );
}
//------------------------------------------------------------------------------------
// Purpose: Perform a query
//------------------------------------------------------------------------------------
bool CSQLAccess::BYieldingExecute( const char *pchName, const char *pchSQLCommand, uint32 *pcRowsAffected, bool bSpewOnError )
{
if ( NULL == pchName )
{
pchName = pchSQLCommand;
}
bool bStandalone = !BInTransaction();
if( bStandalone )
{
BBeginTransaction( pchName );
}
CurrentQuery()->SetCommand( pchSQLCommand );
m_pQueryGroup->AddQuery( m_pCurrentQuery );
m_pCurrentQuery = NULL;
bool bSuccess = true;
if( bStandalone )
{
bSuccess = BCommitTransaction();
if( bSuccess && pcRowsAffected )
{
*pcRowsAffected = m_pQueryGroup->GetResults()->GetRowsAffected( 0 );
}
}
return bSuccess;
}
//------------------------------------------------------------------------------------
// Purpose: Starts a transaction
//------------------------------------------------------------------------------------
bool CSQLAccess::BBeginTransaction( const char *pchName )
{
Assert( !m_bInTransaction );
if( m_bInTransaction )
return false;
m_pQueryGroup->Clear();
m_pQueryGroup->SetName( pchName );
m_bInTransaction = true;
return true;
}
//------------------------------------------------------------------------------------
// Purpose: Returns the string last passed to BBeginTransaction
//------------------------------------------------------------------------------------
const char *CSQLAccess::PchTransactionName( ) const
{
return m_pQueryGroup->PchName();
}
//------------------------------------------------------------------------------------
// Purpose: Commits a transaction to the database
//------------------------------------------------------------------------------------
bool CSQLAccess::BCommitTransaction( bool bAllowEmpty )
{
Assert( BInTransaction() );
if( !BInTransaction() )
return false;
if( !m_pCurrentQuery && !m_pQueryGroup->GetStatementCount() )
{
if( bAllowEmpty )
{
// No-op success
m_bInTransaction = false;
RunListeners_Commit();
return true;
}
else
{
AssertMsg1( false, "BCommitTransaction with empty transaction at %s", m_pQueryGroup->PchName() );
return false;
}
}
AssertMsg1( !m_pCurrentQuery, "Unexecuted query present in BCommitTransaction: %s", m_pCurrentQuery->PchCommand() );
if( m_pCurrentQuery )
return false;
m_bInTransaction = false;
if( !GJobCur().BYieldingRunQuery( m_pQueryGroup, m_eSchemaCatalog ) )
{
// Notify listeners that the transaction did not succeed
RunListeners_Rollback();
return false;
}
// The transaction presumably did make the database, so we do not notify rollback listeners beyond here.
RunListeners_Commit();
if( !m_pQueryGroup->GetResults() )
return false;
return true;
}
//------------------------------------------------------------------------------------
// Purpose: Rolls back a transaction and clears any queries
//------------------------------------------------------------------------------------
void CSQLAccess::RollbackTransaction()
{
bool bWasTransaction = BInTransaction();
Assert( bWasTransaction );
SAFE_DELETE( m_pCurrentQuery );
m_bInTransaction = false;
if ( bWasTransaction )
{
RunListeners_Rollback();
}
else
{
m_vecCommitListeners.clear();
m_vecRollbackListeners.clear();
}
}
//------------------------------------------------------------------------------------
// Purpose: Adds a listener to be called synchronously should the transaction successfully commit
//------------------------------------------------------------------------------------
void CSQLAccess::AddCommitListener( std::function<void (void)> &&listener )
{
if ( !BInTransaction() )
{
AssertMsg( BInTransaction(), "Adding a listener to a non-transaction access, will never fire" );
return;
}
m_vecCommitListeners.push_back( std::move( listener ) );
}
//------------------------------------------------------------------------------------
// Purpose: Adds a listener to be called synchronously should the transaction fail or explicitly rollback
//------------------------------------------------------------------------------------
void CSQLAccess::AddRollbackListener( std::function<void (void)> &&listener )
{
if ( !BInTransaction() )
{
AssertMsg( BInTransaction(), "Adding a listener to a non-transaction access, will never fire" );
return;
}
m_vecRollbackListeners.push_back( std::move( listener ) );
}
//------------------------------------------------------------------------------------
// Purpose: Notifies listeners of successful commit.
//------------------------------------------------------------------------------------
void CSQLAccess::RunListeners_Commit()
{
RunAndClearListenerList( m_vecCommitListeners );
// Clear the unused set
m_vecRollbackListeners.clear();
}
//------------------------------------------------------------------------------------
// Purpose: Notifies listeners of a implicitly or explicitly rolled back transactions and clears the listener list.
//------------------------------------------------------------------------------------
void CSQLAccess::RunListeners_Rollback()
{
RunAndClearListenerList( m_vecRollbackListeners );
// Clear the unused set
m_vecCommitListeners.clear();
}
//------------------------------------------------------------------------------------
// Purpose: Perform a query that returns a single string
//------------------------------------------------------------------------------------
CSQLAccess::EReadSingleResultResult CSQLAccess::BYieldingExecuteSingleResultDataInternal( const char *pchName, const char *pchSQLCommand, EGCSQLType eType, uint8 **ppubData, uint32 *punSize, uint32 *pcRowsAffected, bool bHasDefaultValue )
{
AssertMsg( !BInTransaction(), "BYieldingExecuteSingleResultData is not supported in a transaction" );
if( BInTransaction() )
return eReadSingle_Error;
bool bRet = BYieldingExecute( pchName, pchSQLCommand, pcRowsAffected );
if ( !bRet )
return eReadSingle_Error;
if( m_pQueryGroup->GetResults()->GetResultSetCount() != 1 )
{
AssertMsg1( false, "Expected single result set, found %d", m_pQueryGroup->GetResults()->GetResultSetCount() );
return eReadSingle_Error;
}
IGCSQLResultSet *pResultSet = m_pQueryGroup->GetResults()->GetResultSet( 0 );
// If we have a default value, getting back zero rows is acceptable.
if( pResultSet->GetRowCount() == 0 && bHasDefaultValue )
{
return eReadSingle_UseDefault;
}
// If we either have more than one row or no default value specified, that's an error.
if( pResultSet->GetRowCount() != 1 )
{
AssertMsg1( false, "Expected single result, found %d", pResultSet->GetRowCount() );
return eReadSingle_Error;
}
if( pResultSet->GetColumnCount() != 1 )
{
AssertMsg1( false, "Expected single column, found %d", pResultSet->GetColumnCount() );
return eReadSingle_Error;
}
if( pResultSet->GetColumnType( 0 ) != eType )
{
AssertMsg2( false, "Expected column of type %s, found %s", PchNameFromEGCSQLType( eType ), PchNameFromEGCSQLType( pResultSet->GetColumnType( 0 ) ) );
return eReadSingle_Error;
}
return pResultSet->GetData( 0, 0, ppubData, punSize )
? eReadSingle_ResultFound
: eReadSingle_Error;
}
//------------------------------------------------------------------------------------
// Purpose: Perform a query that returns a single string
//------------------------------------------------------------------------------------
bool CSQLAccess::BYieldingExecuteString( const char *pchName, const char *pchSQLCommand, CFmtStr1024 *psResult, uint32 *pcRowsAffected )
{
uint8 *pubData;
uint32 cubData;
if( CSQLAccess::BYieldingExecuteSingleResultDataInternal( pchName, pchSQLCommand, k_EGCSQLType_String, &pubData, &cubData, pcRowsAffected, false ) != eReadSingle_ResultFound )
return false;
*psResult = (char *)pubData;
return true;
}
//------------------------------------------------------------------------------------
// Purpose: Perform a query that returns a single int
//------------------------------------------------------------------------------------
bool CSQLAccess::BYieldingExecuteScalarInt( const char *pchName, const char *pchSQLCommand, int *pnResult, uint32 *pcRowsAffected )
{
return BYieldingExecuteSingleResult<int32, uint32>( pchName, pchSQLCommand, k_EGCSQLType_int32, pnResult, pcRowsAffected );
}
bool CSQLAccess::BYieldingExecuteScalarIntWithDefault( const char *pchName, const char *pchSQLCommand, int *pnResult, int iDefaultValue, uint32 *pcRowsAffected )
{
return BYieldingExecuteSingleResultWithDefault<int32, uint32>( pchName, pchSQLCommand, k_EGCSQLType_int32, pnResult, iDefaultValue, pcRowsAffected );
}
//------------------------------------------------------------------------------------
// Purpose: Perform a query that returns a single uint32
//------------------------------------------------------------------------------------
bool CSQLAccess::BYieldingExecuteScalarUint32( const char *pchName, const char *pchSQLCommand, uint32 *punResult, uint32 *pcRowsAffected )
{
return BYieldingExecuteSingleResult<uint32, uint32>( pchName, pchSQLCommand, k_EGCSQLType_int32, punResult, pcRowsAffected );
}
bool CSQLAccess::BYieldingExecuteScalarUint32WithDefault( const char *pchName, const char *pchSQLCommand, uint32 *punResult, uint32 unDefaultValue, uint32 *pcRowsAffected )
{
return BYieldingExecuteSingleResultWithDefault<uint32, uint32>( pchName, pchSQLCommand, k_EGCSQLType_int32, punResult, unDefaultValue, pcRowsAffected );
}
//------------------------------------------------------------------------------------
// Purpose: A bunch of pass throughs to the query itself
//------------------------------------------------------------------------------------
void CSQLAccess::AddBindParam( const char *pchValue )
{
CurrentQuery()->AddBindParam( pchValue );
}
void CSQLAccess::AddBindParam( const int16 nValue )
{
CurrentQuery()->AddBindParam( nValue );
}
void CSQLAccess::AddBindParam( const uint16 uValue )
{
CurrentQuery()->AddBindParam( uValue );
}
void CSQLAccess::AddBindParam( const int32 nValue )
{
CurrentQuery()->AddBindParam( nValue );
}
void CSQLAccess::AddBindParam( const uint32 uValue )
{
CurrentQuery()->AddBindParam( uValue );
}
void CSQLAccess::AddBindParam( const uint64 ulValue )
{
CurrentQuery()->AddBindParam( ulValue );
}
void CSQLAccess::AddBindParam( const uint8 *ubValue, const int cubValue )
{
CurrentQuery()->AddBindParam( ubValue, cubValue );
}
void CSQLAccess::AddBindParam( const float fValue )
{
CurrentQuery()->AddBindParam( fValue );
}
void CSQLAccess::AddBindParam( const double dValue )
{
CurrentQuery()->AddBindParam( dValue );
}
void CSQLAccess::AddBindParamRaw( EGCSQLType eType, const byte *pubData, uint32 cubData )
{
CurrentQuery()->AddBindParamRaw( eType, pubData, cubData );
}
void CSQLAccess::ClearParams()
{
if( m_pCurrentQuery )
{
delete m_pCurrentQuery;
m_pCurrentQuery = NULL;
}
}
IGCSQLResultSetList *CSQLAccess::GetResults()
{
return m_pQueryGroup->GetResults();
}
//------------------------------------------------------------------------------------
// Purpose: Returns the number of result sets
//------------------------------------------------------------------------------------
uint32 CSQLAccess::GetResultSetCount()
{
if( m_pQueryGroup->GetResults() )
return m_pQueryGroup->GetResults()->GetResultSetCount();
else
return 0;
}
//------------------------------------------------------------------------------------
// Purpose: Returns the number of rows in a result set
//------------------------------------------------------------------------------------
uint32 CSQLAccess::GetResultSetRowCount( uint32 unResultSet )
{
if( m_pQueryGroup->GetResults() && unResultSet < m_pQueryGroup->GetResults()->GetResultSetCount() )
return m_pQueryGroup->GetResults()->GetResultSet( unResultSet )->GetRowCount();
else
return 0;
}
//------------------------------------------------------------------------------------
// Purpose: Returns a CSQLRecord object that represents a row in a result set
//------------------------------------------------------------------------------------
CSQLRecord CSQLAccess::GetResultRecord( uint32 unResultSet, uint32 unRow )
{
if( m_pQueryGroup->GetResults() && unResultSet < m_pQueryGroup->GetResults()->GetResultSetCount() )
{
IGCSQLResultSet *pResultSet = m_pQueryGroup->GetResults()->GetResultSet( unResultSet );
if( unRow < pResultSet->GetRowCount() )
return CSQLRecord( unRow, pResultSet );
}
return CSQLRecord(); // if there was a problem return an empty record
}
//-----------------------------------------------------------------------------
// Purpose: Inserts a new record into the DS
// Input: pRecordBase - record to insert
// Output: true if successful, false otherwise
//-----------------------------------------------------------------------------
bool CSQLAccess::BYieldingInsertRecord( const CRecordBase *pRecordBase )
{
ClearParams();
const CRecordInfo *pRecordInfo = pRecordBase->GetPSchema()->GetRecordInfo();
int cColumns = pRecordInfo->GetNumColumns();
for ( int nColumn = 0; nColumn < cColumns; nColumn++ )
{
const CColumnInfo &columnInfo = pRecordInfo->GetColumnInfo( nColumn );
if ( !columnInfo.BIsInsertable() )
continue;
uint8 *pubData;
uint32 cubData;
DbgVerify( pRecordBase->BGetField( nColumn, &pubData, &cubData ) );
CurrentQuery()->AddBindParamRaw( columnInfo.GetType(), pubData, cubData );
}
uint32 nRows;
const char *pchStatement = pRecordBase->GetPSchema()->GetInsertStatementText();
bool bRet = BYieldingExecute( pchStatement, pchStatement, &nRows );
return ( nRows == 1 || BInTransaction() ) && bRet;
}
//-----------------------------------------------------------------------------
// Purpose: Inserts a new record into the DS if such row doesn't exist
// Input: pRecordBase - record to insert
// Output: true if successful, false otherwise
//-----------------------------------------------------------------------------
bool CSQLAccess::BYieldingInsertWhenNotMatchedOnPK( CRecordBase *pRecordBase )
{
ClearParams();
const CRecordInfo *pRecordInfo = pRecordBase->GetPSchema()->GetRecordInfo();
int cColumns = pRecordInfo->GetNumColumns();
for ( int nColumn = 0; nColumn < cColumns; nColumn++ )
{
const CColumnInfo &columnInfo = pRecordInfo->GetColumnInfo( nColumn );
if ( !columnInfo.BIsInsertable() )
{
Assert( columnInfo.BIsInsertable() );
return false;
}
uint8 *pubData;
uint32 cubData;
DbgVerify( pRecordBase->BGetField( nColumn, &pubData, &cubData ) );
CurrentQuery()->AddBindParamRaw( columnInfo.GetType(), pubData, cubData );
}
uint32 nRows;
const char *pchStatement = pRecordBase->GetPSchema()->GetMergeStatementTextOnPKWhenNotMatchedInsert();
bool bRet = BYieldingExecute( pchStatement, pchStatement, &nRows );
return ( nRows == 1 || nRows == 0 || BInTransaction() ) && bRet;
}
//-----------------------------------------------------------------------------
// Purpose: Inserts a new record into the DS if such row doesn't exist
// updates an existing row if such row is matched by PK
// Input: pRecordBase - record to insert
// Output: true if successful, false otherwise
//-----------------------------------------------------------------------------
bool CSQLAccess::BYieldingInsertOrUpdateOnPK( CRecordBase *pRecordBase )
{
ClearParams();
const CRecordInfo *pRecordInfo = pRecordBase->GetPSchema()->GetRecordInfo();
int cColumns = pRecordInfo->GetNumColumns();
for ( int nColumn = 0; nColumn < cColumns; nColumn++ )
{
const CColumnInfo &columnInfo = pRecordInfo->GetColumnInfo( nColumn );
if ( !columnInfo.BIsInsertable() )
{
Assert( columnInfo.BIsInsertable() );
return false;
}
uint8 *pubData;
uint32 cubData;
DbgVerify( pRecordBase->BGetField( nColumn, &pubData, &cubData ) );
CurrentQuery()->AddBindParamRaw( columnInfo.GetType(), pubData, cubData );
}
uint32 nRows;
const char *pchStatement = pRecordBase->GetPSchema()->GetMergeStatementTextOnPKWhenMatchedUpdateWhenNotMatchedInsert();
bool bRet = BYieldingExecute( pchStatement, pchStatement, &nRows );
return ( nRows == 1 || BInTransaction() ) && bRet;
}
//-----------------------------------------------------------------------------
// 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
//-----------------------------------------------------------------------------
bool CSQLAccess::BYieldingInsertWithIdentity( CRecordBase* pRecordBase )
{
AssertMsg( !BInTransaction(), "BYieldingInsertWithIdentity is not supported in a transaction" );
if( BInTransaction() )
return false;
ClearParams();
TSQLCmdStr sStatement;
CUtlVector<int> vecOutputFields;
CRecordInfo *pRecordInfo = pRecordBase->GetPSchema()->GetRecordInfo();
BuildInsertAndReadStatementText( &sStatement, &vecOutputFields, pRecordInfo );
AssertMsg( vecOutputFields.Count() > 0, "BYieldingInsertAndReadRecord called for a record type with no non-insertable columns" );
if ( vecOutputFields.Count() == 0 )
return false;
int cColumns = pRecordInfo->GetNumColumns();
for ( int nColumn = 0; nColumn < cColumns; nColumn++ )
{
const CColumnInfo &columnInfo = pRecordInfo->GetColumnInfo( nColumn );
if ( !columnInfo.BIsInsertable() )
{
continue;
}
uint8 *pubData;
uint32 cubData;
DbgVerify( pRecordBase->BGetField( nColumn, &pubData, &cubData ) );
CurrentQuery()->AddBindParamRaw( columnInfo.GetType(), pubData, cubData );
}
bool bRet = BYieldingExecute( sStatement, sStatement );
if( !bRet )
return false;
Assert( 1 == GetResultSetCount() );
if ( 1 != GetResultSetCount() )
return false;
IGCSQLResultSet *pResultSet = m_pQueryGroup->GetResults()->GetResultSet( 0 );
Assert( 1 == pResultSet->GetRowCount() );
if ( 1 != pResultSet->GetRowCount() )
return false;
Assert( (uint32)vecOutputFields.Count() == pResultSet->GetColumnCount() );
if ( (uint32)vecOutputFields.Count() != pResultSet->GetColumnCount() )
return false;
for( uint32 nColumn = 0; nColumn < pResultSet->GetColumnCount(); nColumn++ )
{
uint8 *pubData;
uint32 cubData;
DbgVerify( pResultSet->GetData( 0, nColumn, &pubData, &cubData ) );
int nSchColumn = vecOutputFields[nColumn];
Assert( pResultSet->GetColumnType( nColumn ) == pRecordInfo->GetColumnInfo( nSchColumn ).GetType() );
DbgVerify( pRecordBase->BSetField( nSchColumn, pubData, cubData ) );
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Reads a list of records from the DB according to the specified where
// clause
// Input: pRecordBase - record to read
// readSet - The set of columns to read
// whereSet - The set of columns to query on
// Output: true if successful, false otherwise
//-----------------------------------------------------------------------------
EResult CSQLAccess::YieldingReadRecordWithWhereColumns( CRecordBase *pRecord, const CColumnSet & readSet, const CColumnSet & whereSet, const char* pchOrderClause )
{
AssertMsg( !BInTransaction(), "BYieldingReadRecordWithWhereColumns is not supported in a transaction" );
if( BInTransaction() )
return k_EResultInvalidState;
//if there is an order by clause, only take the top one, if there isn't, then validate that we have a single instance
const char* pszTopClause = ( pchOrderClause ) ? "TOP (1)" : "TOP (2)";
TSQLCmdStr sStatement;
BuildSelectStatementText( &sStatement, readSet, pszTopClause );
// if we actually have some columns for the where clause,
// append a where clause.
if( whereSet.GetColumnCount() )
{
sStatement.Append( " WHERE " );
AppendWhereClauseText( &sStatement, whereSet );
AddRecordParameters( *pRecord, whereSet );
}
//append the order by if they added one
if( pchOrderClause )
{
sStatement.Append( " ORDER BY " );
sStatement.Append( pchOrderClause );
}
Assert(!readSet.IsEmpty() );
if( !BYieldingExecute( sStatement, sStatement ) )
return k_EResultFail;
if ( GetResultSetCount() != 1 )
{
AssertMsg( GetResultSetCount() == 1, "Unexpected number of result sets returned from select statement" );
return k_EResultFail;
}
// make sure the types are the same
IGCSQLResultSet *pResultSet = m_pQueryGroup->GetResults()->GetResultSet( 0 );
if ( pResultSet->GetRowCount() == 0 )
return k_EResultNoMatch;
//note that since we only take the top one when there is an order by clause, we don't need to handle that case down here, only if top 2 is selected
if( pResultSet->GetRowCount() != 1 )
{
// Make sure we aren't failing because there are multiple matching records.
// That is probably a misuse of the API or some unexpected condition.
AssertMsg1( false, "BYieldingReadRecordWithWhereColumns from %s failing because multiple records match WHERE clause", readSet.GetRecordInfo()->GetName() );
return k_EResultLimitExceeded;
}
FOR_EACH_COLUMN_IN_SET( readSet, nColumnIndex )
{
EGCSQLType eRecordType = readSet.GetColumnInfo( nColumnIndex ).GetType();
EGCSQLType eResultType = pResultSet->GetColumnType( nColumnIndex );
AssertMsg2( eResultType == eRecordType, "Column %d type mismatch in %s", nColumnIndex, readSet.GetRecordInfo()->GetName() );
if( eRecordType != eResultType )
return k_EResultInvalidParam;
}
CSQLRecord sqlRecord = GetResultRecord( 0, 0 );
FOR_EACH_COLUMN_IN_SET( readSet, nColumnIndex )
{
uint8 *pubData;
uint32 cubData;
DbgVerify( sqlRecord.BGetColumnData( nColumnIndex, &pubData, (int*)&cubData ) );
DbgVerify( pRecord->BSetField( readSet.GetColumn( nColumnIndex), pubData, cubData ) );
}
return k_EResultOK;
}
//-----------------------------------------------------------------------------
// Purpose: Updates a record in the DB
// Input: record - data source for columns to match against (whereColumns) and
// columns to assign (updateColumns)
// whereColumns - columns to match against
// updateColumns - columns to update
// Output: true if successful, false otherwise
//-----------------------------------------------------------------------------
bool CSQLAccess::BYieldingUpdateRecord( const CRecordBase & record, const CColumnSet & whereColumns, const CColumnSet & updateColumns, const CSQLOutputParams *pOptionalOutputParams /* = NULL */ )
{
return BYieldingUpdateRecords( record, whereColumns, record, updateColumns, pOptionalOutputParams );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CSQLAccess::BYieldingUpdateRecords( const CRecordBase & whereRecord, const CColumnSet & whereColumns, const CRecordBase & updateRecord, const CColumnSet & updateColumns, const CSQLOutputParams *pOptionalOutputParams /* = NULL */ )
{
ClearParams();
Assert( whereColumns.GetRecordInfo() == updateColumns.GetRecordInfo() );
if ( whereColumns.GetRecordInfo() != updateColumns.GetRecordInfo() )
return false;
Assert( whereColumns.GetRecordInfo() == whereRecord.GetPSchema()->GetRecordInfo() );
if ( whereColumns.GetRecordInfo() != whereRecord.GetPSchema()->GetRecordInfo() )
return false;
Assert( whereColumns.GetRecordInfo() == updateRecord.GetPSchema()->GetRecordInfo() );
if ( whereColumns.GetRecordInfo() != updateRecord.GetPSchema()->GetRecordInfo() )
return false;
AssertMsg( !updateColumns.IsEmpty(), "Someone is calling BYieldingUpdateRecord with no columns to update." );
if ( updateColumns.IsEmpty() )
return false;
// add the columns we're updating as bound params
TSQLCmdStr sStatement;
BuildUpdateStatementText( &sStatement, updateColumns );
AddRecordParameters( updateRecord, updateColumns );
// did the users specify an OUTPUT block?
if ( pOptionalOutputParams )
{
TSQLCmdStr sOutput;
BuildOutputClauseText( &sOutput, pOptionalOutputParams->GetColumnSet() );
sStatement.Append( sOutput );
AddRecordParameters( pOptionalOutputParams->GetRecord(), pOptionalOutputParams->GetColumnSet() );
}
if ( !whereColumns.IsEmpty() )
{
sStatement.Append( " WHERE " );
AppendWhereClauseText( &sStatement, whereColumns );
// add the columns we're querying on as bound params
AddRecordParameters( whereRecord, whereColumns );
}
return BYieldingExecute( sStatement, sStatement );
}
//-----------------------------------------------------------------------------
// Purpose: Deletes this record's row in the table
// Input: record - record to delete
// whereColumns - columns to use when searching for this record
//-----------------------------------------------------------------------------
bool CSQLAccess::BYieldingDeleteRecords( const CRecordBase & record, const CColumnSet & whereColumns )
{
Assert( whereColumns.GetRecordInfo() == record.GetPSchema()->GetRecordInfo() );
if ( whereColumns.GetRecordInfo() != record.GetPSchema()->GetRecordInfo() )
return false;
ClearParams();
AddRecordParameters( record, whereColumns );
TSQLCmdStr sStatement;
BuildDeleteStatementText( &sStatement, record.GetPRecordInfo() );
sStatement.Append( " WHERE " );
AppendWhereClauseText( &sStatement, whereColumns );
uint32 unRowsAffected;
if( !BYieldingExecute( sStatement, sStatement, &unRowsAffected ) )
return false;
return unRowsAffected > 0 || BInTransaction();
}
//--------------------------------------------------------------------------------------------------------------------------------
// CSQLUpdateOrInsert
//--------------------------------------------------------------------------------------------------------------------------------
CSQLUpdateOrInsert::CSQLUpdateOrInsert( const char* pszName, int nTable, const CColumnSet & whereColumns, const CColumnSet & updateColumns, const char* pszWhereClause, const char* pszUpdateClause )
{
const CRecordInfo* pRecordInfo = GSchemaFull().GetSchema( nTable ).GetRecordInfo();
//how many columns do we have
const int nNumColumns = pRecordInfo->GetNumColumns();
TSQLCmdStr sStatement;
sStatement = "MERGE INTO ";
sStatement.Append( GSchemaFull().GetDefaultSchemaNameForCatalog( pRecordInfo->GetESchemaCatalog() ) );
sStatement.Append( '.' );
sStatement.Append( pRecordInfo->GetName() );
sStatement.Append( " WITH(HOLDLOCK) AS D USING(VALUES(" );
sStatement.AppendFormat( "%.*s", GetInsertArgStringChars( nNumColumns ), GetInsertArgString() );
sStatement.Append( "))AS S(" );
//add each column that we are adding the values for, along with the parameter from the structure
for( int nCurrColumn = 0; nCurrColumn < nNumColumns; nCurrColumn++ )
{
const CColumnInfo& colInfo = pRecordInfo->GetColumnInfo( nCurrColumn );
if( nCurrColumn != 0 )
sStatement.Append( ',' );
sStatement.Append( colInfo.GetName() );
}
//our where clause
sStatement.Append( ")ON " );
if( pszWhereClause )
{
sStatement.Append( pszWhereClause );
}
else
{
FOR_EACH_COLUMN_IN_SET( whereColumns, nCurrColumn )
{
const char* pszColName = pRecordInfo->GetColumnInfo( whereColumns.GetColumn( nCurrColumn ) ).GetName();
if( nCurrColumn > 0 )
sStatement.Append( " AND " );
sStatement.AppendFormat( "D.%s=S.%s", pszColName, pszColName );
}
}
//our update clause (if they have provided fields that they want to update)
if( pszUpdateClause || !updateColumns.IsEmpty() )
{
sStatement.Append( " WHEN MATCHED THEN UPDATE SET " );
if( pszUpdateClause )
{
sStatement.Append( pszUpdateClause );
}
else
{
FOR_EACH_COLUMN_IN_SET( updateColumns, nCurrColumn )
{
const char* pszColName = pRecordInfo->GetColumnInfo( updateColumns.GetColumn( nCurrColumn ) ).GetName();
if( nCurrColumn > 0 )
sStatement.Append( ',' );
sStatement.AppendFormat( "%s=S.%s", pszColName, pszColName );
}
}
}
//our insert clause
sStatement.Append( " WHEN NOT MATCHED THEN INSERT(" );
bool bFirstColumn = true;
for( int nCurrColumn = 0; nCurrColumn < nNumColumns; nCurrColumn++ )
{
const CColumnInfo& colInfo = pRecordInfo->GetColumnInfo( nCurrColumn );
if( !colInfo.BIsInsertable() )
continue;
if( !bFirstColumn )
sStatement.Append( ',' );
bFirstColumn = false;
sStatement.Append( colInfo.GetName() );
}
sStatement.Append( ")VALUES(" );
bFirstColumn = true;
for( int nCurrColumn = 0; nCurrColumn < nNumColumns; nCurrColumn++ )
{
const CColumnInfo& colInfo = pRecordInfo->GetColumnInfo( nCurrColumn );
if( !colInfo.BIsInsertable() )
continue;
if( !bFirstColumn )
sStatement.Append( ',' );
bFirstColumn = false;
sStatement.AppendFormat( "S.%s", colInfo.GetName() );
}
sStatement.Append( ");" );
//save our results so we can execute it in the future
m_nTable = nTable;
m_sName = pszName;
m_sQuery = sStatement;
}
bool CSQLUpdateOrInsert::BYieldingExecute( CSQLAccess& sqlAccess, const CRecordBase& record, uint32 *out_punRowsAffected /* = NULL */ ) const
{
AssertMsg2( record.GetITable() == m_nTable, "Error: Merge was compiled for table %s, but was attempted to be executed against %s", GSchemaFull().GetSchema( m_nTable ).GetRecordInfo()->GetName(), record.GetPRecordInfo()->GetName() );
const CRecordInfo* pRecordInfo = record.GetPRecordInfo();
//how many columns do we have
const int nNumColumns = pRecordInfo->GetNumColumns();
sqlAccess.ClearParams();
for( int nCurrColumn = 0; nCurrColumn < nNumColumns; nCurrColumn++ )
{
const CColumnInfo& colInfo = pRecordInfo->GetColumnInfo( nCurrColumn );
uint8 *pubData;
uint32 cubData;
DbgVerify( record.BGetField( nCurrColumn, &pubData, &cubData ) );
sqlAccess.AddBindParamRaw( colInfo.GetType(), pubData, cubData );
}
return sqlAccess.BYieldingExecute( m_sName, m_sQuery, out_punRowsAffected );
}
//-----------------------------------------------------------------------------
// Purpose: Adds bind parameters to the list based on a set of fields in a record
// Input: record - record to insert
// columnSet - The set of columns to add as params
//-----------------------------------------------------------------------------
void CSQLAccess::AddRecordParameters( const CRecordBase &record, const CColumnSet & columnSet )
{
Assert( record.GetPSchema()->GetRecordInfo() == columnSet.GetRecordInfo() );
if ( record.GetPSchema()->GetRecordInfo() != columnSet.GetRecordInfo() )
return;
FOR_EACH_COLUMN_IN_SET( columnSet, nColumnIndex )
{
const CColumnInfo &columnInfo = columnSet.GetColumnInfo( nColumnIndex );
uint8 *pubData;
uint32 cubData;
DbgVerify( record.BGetField( columnSet.GetColumn( nColumnIndex ), &pubData, &cubData ) );
EGCSQLType eType = columnInfo.GetType();
CurrentQuery()->AddBindParamRaw( eType, pubData, cubData );
}
}
//-----------------------------------------------------------------------------
// Purpose: Deletes all records from a table
// Input: iTable - table to wipe
// Output: true if the operation was successful
// Note: PERFORMANCE WARNING: this is slow on big tables, not intended for use
// in production
//-----------------------------------------------------------------------------
bool CSQLAccess::BYieldingWipeTable( int iTable )
{
// make a wipe operation
CRecordInfo *pRecordInfo = GSchemaFull().GetSchema( iTable ).GetRecordInfo();
CUtlString buf;
buf.Format( "DELETE FROM %s", pRecordInfo->GetName() );
return BYieldingExecute( buf.String(), buf.String() );
}
//-----------------------------------------------------------------------------
// Purpose: Returns the current query to add stuff to, creating it if there isn't
// already a current query
//-----------------------------------------------------------------------------
CGCSQLQuery *CSQLAccess::CurrentQuery()
{
if( m_pCurrentQuery )
return m_pCurrentQuery;
m_pCurrentQuery = new CGCSQLQuery();
return m_pCurrentQuery;
}
} // namespace GCSDK
+538
View File
@@ -0,0 +1,538 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================
#include "stdafx.h"
//#include "sqlaccess.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
namespace GCSDK
{
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CSQLRecord::CSQLRecord( uint32 unRow, IGCSQLResultSet *pResultSet )
{
Init( unRow, pResultSet );
}
CSQLRecord::CSQLRecord()
: m_pResultSet( NULL ), m_unRow( 0 )
{
}
//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
CSQLRecord::~CSQLRecord()
{
}
//-----------------------------------------------------------------------------
// Purpose: Initializes a blank record
// Input: iTable - table that this record will belong to
//-----------------------------------------------------------------------------
void CSQLRecord::Init( uint32 unRow, IGCSQLResultSet *pResultSet )
{
if( unRow >= pResultSet->GetRowCount() )
{
m_pResultSet = NULL;
m_unRow = 0;
}
else
{
m_pResultSet = pResultSet;
m_unRow = unRow;
}
}
//-----------------------------------------------------------------------------
// Purpose: Gets data for a field in this record
// Input: unColumn - field to get
// pubField - pointer to get filled in with pointer to data
// cubField - pointer to get filled in with size of data
// Output: true if successful, false if data not present
//-----------------------------------------------------------------------------
bool CSQLRecord::BGetColumnData( uint32 unColumn, uint8 **ppubField, int *pcubField )
{
size_t sz;
bool bRet = BGetColumnData( unColumn, ppubField, &sz );
*pcubField = static_cast< int >( sz );
return bRet;
}
//-----------------------------------------------------------------------------
// Purpose: Gets data for a field in this record
// Input: unColumn - field to get
// pubField - pointer to get filled in with pointer to data
// cubField - pointer to get filled in with size of data
// Output: true if successful, false if data not present
//-----------------------------------------------------------------------------
bool CSQLRecord::BGetColumnData( uint32 unColumn, uint8 **ppubField, size_t *pcubField )
{
Assert( ppubField );
Assert( pcubField );
*ppubField = NULL;
*pcubField = 0;
Assert( m_pResultSet );
if ( !BValidateColumnIndex( unColumn ) )
return false;
*pcubField = 0;
return m_pResultSet->GetData( m_unRow, unColumn, ppubField, (uint32*)pcubField );
}
//-----------------------------------------------------------------------------
// Purpose: Gets string data for a field in this record
// Input: unColumn - field to get
// ppchVal - pointer to pointer to fill in to string data
// Output: true if successful, false if data not present or not of correct type
//-----------------------------------------------------------------------------
bool CSQLRecord::BGetStringValue( uint32 unColumn, const char **ppchVal )
{
Assert( ppchVal );
*ppchVal = NULL;
uint8 *pubData = NULL;
int cubData = 0;
Assert( k_EGCSQLType_String == m_pResultSet->GetColumnType( unColumn ) );
bool bRet = BGetColumnData( unColumn, &pubData, &cubData );
if ( bRet )
*ppchVal = (const char *) pubData;
return bRet;
}
//-----------------------------------------------------------------------------
// Purpose: Gets string data for a field in this record
// Input: unColumn - field to get
// ppchVal - pointer to pointer to fill in to string data
// Output: true if successful, false if data not present or not of correct type
//-----------------------------------------------------------------------------
bool CSQLRecord::BGetStringValue( uint32 unColumn, CFmtStr1024 *psVal )
{
Assert( psVal );
*psVal = "";
uint8 *pubData = NULL;
int cubData = 0;
Assert( k_EGCSQLType_String == m_pResultSet->GetColumnType( unColumn ) );
bool bRet = BGetColumnData( unColumn, &pubData, &cubData );
if ( bRet )
*psVal = (const char *) pubData;
return bRet;
}
//-----------------------------------------------------------------------------
// Purpose: Gets int data for a field in this record
// Input: unColumn - field to get
// pnVal - pointer to fill in with data
// Output: true if successful, false if data not present or not of correct type
//-----------------------------------------------------------------------------
bool CSQLRecord::BGetIntValue( uint32 unColumn, int *pnVal )
{
Assert( pnVal );
*pnVal = 0;
uint8 *pubData = NULL;
int cubData = 0;
bool bRet = BGetColumnData( unColumn, &pubData, &cubData );
if ( bRet )
{
switch( m_pResultSet->GetColumnType( unColumn ) )
{
case k_EGCSQLType_int64:
{
int64 ul = *((int64 *)pubData);
if ( ul >= LONG_MIN && ul <= LONG_MAX )
{
*pnVal = (int)ul;
return true;
}
else
{
AssertMsg1(false, "GetIntValue tried to catch %lld in an int, which is too small", ul );
return false;
}
}
break;
case k_EGCSQLType_int32:
*pnVal = *((int32 *)pubData);
return true;
case k_EGCSQLType_int16:
*pnVal = *((int16 *)pubData);
return true;
case k_EGCSQLType_int8:
*pnVal = *((int8 *)pubData);
return true;
default:
AssertMsg1(false, "GetIntValue tried to catch a %s, which is the wrong type", PchNameFromEGCSQLType( m_pResultSet->GetColumnType( unColumn ) ) );
return false;
}
}
return bRet;
}
//-----------------------------------------------------------------------------
// Purpose: Gets float data for a field in this record
// Input: unColumn - field to get
// pnVal - pointer to fill in with data
// Output: true if successful, false if data not present or not of correct type
//-----------------------------------------------------------------------------
bool CSQLRecord::BGetFloatValue( uint32 unColumn, float *pfVal )
{
Assert( pfVal );
*pfVal = 0.0f;
uint8 *pubData = NULL;
int cubData = 0;
bool bRet = BGetColumnData( unColumn, &pubData, &cubData );
if ( bRet )
{
Assert( k_EGCSQLType_float == m_pResultSet->GetColumnType( unColumn ) );
AssertMsg2( sizeof( float ) == cubData, "GetValue expected %llu bytes, found %d", (uint64)sizeof( float ), cubData );
if ( sizeof( float ) != cubData )
return false;
*pfVal = *( (float *) pubData );
}
return bRet;
}
//-----------------------------------------------------------------------------
// Purpose: Gets double data for a field in this record
// Input: unColumn - field to get
// pnVal - pointer to fill in with data
// Output: true if successful, false if data not present or not of correct type
//-----------------------------------------------------------------------------
bool CSQLRecord::BGetDoubleValue( uint32 unColumn, double *pdVal )
{
Assert( pdVal );
*pdVal = 0.0f;
uint8 *pubData = NULL;
int cubData = 0;
bool bRet = BGetColumnData( unColumn, &pubData, &cubData );
if ( bRet )
{
Assert( k_EGCSQLType_double == m_pResultSet->GetColumnType( unColumn ) );
AssertMsg2( sizeof( double ) == cubData, "GetValue expected %llu bytes, found %d", (uint64)sizeof( double ), cubData );
if ( sizeof( double ) != cubData )
return false;
*pdVal = *( (double *) pubData );
}
return bRet;
}
//-----------------------------------------------------------------------------
// Purpose: Gets int data for a field in this record
// Input: unColumn - field to get
// pVal - pointer to fill in with data
// Output: true if successful, false if data not present or not of correct type
//-----------------------------------------------------------------------------
bool CSQLRecord::BGetByteValue( uint32 unColumn, byte *pVal )
{
Assert( pVal );
*pVal = 0;
uint8 *pubData = NULL;
int cubData = 0;
bool bRet = BGetColumnData( unColumn, &pubData, &cubData );
if ( bRet )
{
Assert( k_EGCSQLType_int8 == m_pResultSet->GetColumnType( unColumn ) );
AssertMsg1( 1 == cubData, "GetValue expected 1 bytes, found %d", cubData );
if ( 1 != cubData )
return false;
*pVal = *( (byte *) pubData );
}
return bRet;
}
//-----------------------------------------------------------------------------
// Purpose: Gets int data for a field in this record
// Input: unColumn - field to get
// pVal - pointer to fill in with data
// Output: true if successful, false if data not present or not of correct type
//-----------------------------------------------------------------------------
bool CSQLRecord::BGetBoolValue( uint32 unColumn, bool *pVal )
{
int32 b;
if ( !BGetIntValue( unColumn, &b ) )
return false;
// convert to boolean
*pVal = ( b != 0 );
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Gets int16 data for a field in this record
// Input: unColumn - field to get
// pnVal - pointer to fill in with data
// Output: true if successful, false if data not present or not of correct type
//-----------------------------------------------------------------------------
bool CSQLRecord::BGetInt16Value( uint32 unColumn, int16 *pnVal )
{
Assert( pnVal );
*pnVal = 0;
uint8 *pubData = NULL;
int cubData = 0;
bool bRet = BGetColumnData( unColumn, &pubData, &cubData );
if ( bRet )
{
Assert( k_EGCSQLType_int16 == m_pResultSet->GetColumnType( unColumn ) );
AssertMsg1( 2 == cubData, "GetValue expected 2 bytes, found %d", cubData );
if ( 2 != cubData )
return false;
*pnVal = *( (int16 *) pubData );
}
return bRet;
}
//-----------------------------------------------------------------------------
// Purpose: Gets int64 data for a field in this record
// Input: unColumn - field to get
// puVal - pointer to fill in with data
// Output: true if successful, false if data not present or not of correct type
//-----------------------------------------------------------------------------
bool CSQLRecord::BGetInt64Value( uint32 unColumn, int64 *puVal )
{
Assert( puVal );
*puVal = 0;
uint8 *pubData = NULL;
int cubData = 0;
bool bRet = BGetColumnData( unColumn, &pubData, &cubData );
if ( bRet )
{
Assert( k_EGCSQLType_int64 == m_pResultSet->GetColumnType( unColumn ) );
AssertMsg1( 8 == cubData, "GetValue expected 8 bytes, found %d", cubData );
if ( 8 != cubData )
return false;
*puVal = *( (int64 *) pubData );
}
return bRet;
}
//-----------------------------------------------------------------------------
// Purpose: Gets uint64 data for a field in this record
// Input: unColumn - field to get
// puVal - pointer to fill in with data
// Output: true if successful, false if data not present or not of correct type
//-----------------------------------------------------------------------------
bool CSQLRecord::BGetUint64Value( uint32 unColumn, uint64 *puVal )
{
Assert( puVal );
*puVal = 0;
uint8 *pubData = NULL;
int cubData = 0;
bool bRet = BGetColumnData( unColumn, &pubData, &cubData );
if ( bRet )
{
Assert( k_EGCSQLType_int64 == m_pResultSet->GetColumnType( unColumn ) );
AssertMsg1( 8 == cubData, "GetValue expected 8 bytes, found %d", cubData );
if ( 8 != cubData )
return false;
*puVal = *( (uint64 *) pubData );
}
return bRet;
}
//-----------------------------------------------------------------------------
// Purpose: Gets uint32 data for a field in this record
// Input: unColumn - field to get
// puVal - pointer to fill in with data
// Output: true if successful, false if data not present or not of correct type
//-----------------------------------------------------------------------------
bool CSQLRecord::BGetUint32Value( uint32 unColumn, uint32 *puVal )
{
Assert( puVal );
*puVal = 0;
uint8 *pubData = NULL;
int cubData = 0;
bool bRet = BGetColumnData( unColumn, &pubData, &cubData );
if ( bRet )
{
Assert( k_EGCSQLType_int32 == m_pResultSet->GetColumnType( unColumn ) );
AssertMsg1( 4 == cubData, "GetValue expected 4 bytes, found %d", cubData );
if ( 4 != cubData )
return false;
*puVal = *( (uint32 *) pubData );
}
return bRet;
}
//-----------------------------------------------------------------------------
// Purpose: Gets uint16 data for a field in this record
// Input: unColumn - field to get
// puVal - pointer to fill in with data
// Output: true if successful, false if data not present or not of correct type
//-----------------------------------------------------------------------------
bool CSQLRecord::BGetUint16Value( uint32 unColumn, uint16 *puVal )
{
Assert( puVal );
*puVal = 0;
uint8 *pubData = NULL;
int cubData = 0;
bool bRet = BGetColumnData( unColumn, &pubData, &cubData );
if ( bRet )
{
Assert( k_EGCSQLType_int16 == m_pResultSet->GetColumnType( unColumn ) );
AssertMsg1( 2 == cubData, "GetValue expected 2 bytes, found %d", cubData );
if ( 2 != cubData )
return false;
*puVal = *( (uint16 *) pubData );
}
return bRet;
}
//-----------------------------------------------------------------------------
// Purpose: Gets uint8 data for a field in this record
// Input: unColumn - field to get
// puVal - pointer to fill in with data
// Output: true if successful, false if data not present or not of correct type
//-----------------------------------------------------------------------------
bool CSQLRecord::BGetUint8Value( uint32 unColumn, uint8 *puVal )
{
Assert( puVal );
*puVal = 0;
uint8 *pubData = NULL;
int cubData = 0;
bool bRet = BGetColumnData( unColumn, &pubData, &cubData );
if ( bRet )
{
Assert( k_EGCSQLType_int8 == m_pResultSet->GetColumnType( unColumn ) );
AssertMsg1( 1 == cubData, "GetValue expected 1 byte, found %d", cubData );
if ( 1 != cubData )
return false;
*puVal = *( (uint8 *) pubData );
}
return bRet;
}
//-----------------------------------------------------------------------------
// Purpose: Validates column index
// Input: unColumn - field to validate
// Output: true if valid, false otherwise
//-----------------------------------------------------------------------------
bool CSQLRecord::BValidateColumnIndex( uint32 unColumn )
{
if ( unColumn >= m_pResultSet->GetColumnCount() )
{
AssertMsg2( false, "CSQLRecord::BValidateColumnIndex: invalid column index %d. # columns: %d", unColumn,
m_pResultSet->GetColumnCount() );
return false;
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Advances the CSQLRecord to the next row
// Output: returns false if this call would advance the record past the last row.
// And makes the record invalid.
//-----------------------------------------------------------------------------
bool CSQLRecord::NextRow()
{
Assert( m_pResultSet );
m_unRow++;
if( m_unRow >= m_pResultSet->GetRowCount() )
m_pResultSet = NULL;
return IsValid();
}
//-----------------------------------------------------------------------------
// Purpose: Render a field to a buffer
// Input: unColumn - field to render
// cchBuffer - size of render buffer
// pchBuffer - buffer to render into
//-----------------------------------------------------------------------------
void CSQLRecord::RenderField( uint32 unColumn, int cchBuffer, char *pchBuffer )
{
Q_strncpy( pchBuffer, "", cchBuffer );
uint8 *pubData;
int cubData;
if ( !BGetColumnData( unColumn, &pubData, &cubData ) )
return;
// Get the column info and figure out how to interpret the data
ConvertFieldToText( m_pResultSet->GetColumnType( unColumn ), pubData, cubData, pchBuffer, cchBuffer, false );
}
//-----------------------------------------------------------------------------
// Purpose: Copies a CSQLRecord to CRecordBase
//-----------------------------------------------------------------------------
bool CSQLRecord::BWriteToRecord( CRecordBase *pRecord, const CColumnSet & csWriteFields )
{
bool bSuccess = true;
FOR_EACH_COLUMN_IN_SET( csWriteFields, unSQLColumn )
{
uint32 unRecordColumn = csWriteFields.GetColumn( unSQLColumn );
uint8 *pubData;
size_t cubData;
if( !BGetColumnData( unSQLColumn, &pubData, &cubData ) )
{
bSuccess = false;
}
else
{
bSuccess = pRecord->BSetField( unRecordColumn, pubData, cubData ) && bSuccess;
}
}
return bSuccess;
}
} // namespace GCSDK
+918
View File
@@ -0,0 +1,918 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================
#include "stdafx.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
namespace GCSDK
{
const char *GetInsertArgString()
{
static char s_str[1024];
static bool s_bInit = false;
if ( !s_bInit )
{
for ( int i = 0; i < 1023; i++ )
{
s_str[i] = i % 2 == 0 ? '?' : ',';
}
s_str[1023] = NULL;
s_bInit = true;
}
return s_str;
}
uint32 GetInsertArgStringChars( uint32 nNumParams )
{
AssertMsg( nNumParams <= GetInsertArgStringMaxParams(), "Error: Requested more characters than are provided by the GetInsertArgString" );
if( nNumParams == 0 )
return 0;
return nNumParams * 2 - 1;
}
uint32 GetInsertArgStringMaxParams()
{
return 512;
}
//-----------------------------------------------------------------------------
// Purpose: Converts array of field data to text for SQL IN clause
// Input: columnInfo - schema of column being converted
// pubData - pointer to array of data to convert
// cubData - size of array of data
// rgchResult - pointer to output buffer
// cubResultLen - size of output buffer
// bForPreparedStatement - Should we prepare the text for a prepared statement or directly place the values?
//-----------------------------------------------------------------------------
void ConvertFieldArrayToInText( const CColumnInfo &columnInfo, byte *pubData, int cubData, char *rgchResult, int cubResultLen, bool bForPreparedStatement )
{
int32 cubLength = columnInfo.GetFixedSize();
Assert( cubData % cubLength == 0 );
int32 nArrayCount = cubData / cubLength;
int32 len = 0;
rgchResult[len++] = '(';
for( int i = 0; i < nArrayCount; ++i )
{
if ( bForPreparedStatement )
{
if ( i < nArrayCount - 1 )
{
rgchResult[len++] = '?';
rgchResult[len++] = ',';
}
else
{
rgchResult[len++] = '?';
rgchResult[len++] = ')';
}
}
else
{
switch ( columnInfo.GetType() )
{
case k_EGCSQLType_int8:
if ( i < nArrayCount - 1 )
len += Q_snprintf( rgchResult + len, cubResultLen - len, "%d,", *( (byte *) pubData ) );
else
len += Q_snprintf( rgchResult + len, cubResultLen - len, "%d)", *( (byte *) pubData ) );
break;
case k_EGCSQLType_int16:
if ( i < nArrayCount - 1 )
len += Q_snprintf( rgchResult + len, cubResultLen - len, "%d,", *( (short *) pubData ) );
else
len += Q_snprintf( rgchResult + len, cubResultLen - len, "%d)", *( (short *) pubData ) );
break;
case k_EGCSQLType_int32:
if ( i < nArrayCount - 1 )
len += Q_snprintf( rgchResult + len, cubResultLen - len, "%d,", *( (int *) pubData ) );
else
len += Q_snprintf( rgchResult + len, cubResultLen - len, "%d)", *( (int *) pubData ) );
break;
case k_EGCSQLType_int64:
if ( i < nArrayCount - 1 )
len += Q_snprintf( rgchResult + len, cubResultLen - len, "%lld,", *( (int64 *) pubData ) );
else
len += Q_snprintf( rgchResult + len, cubResultLen - len, "%lld)", *( (int64 *) pubData ) );
break;
default:
AssertMsg( false, "Unsupported data type for non prepares statement with IN clause\n" );
rgchResult[0] = 0;
return;
}
}
if( len >= cubResultLen - 1 )
{
AssertMsg( false, "Generation of IN clause foverflowed\n" );
rgchResult[0] = 0;
return;
}
pubData += cubLength;
}
rgchResult[len] = 0;
return;
}
//-----------------------------------------------------------------------------
// Purpose: Converts field data to text equivalent for SQL statement
// Input: eFieldType - The type of the field to convert to text
// pubRecord - pointer to record data to convert
// cubRecord - size of record data
// rgchField - pointer to output buffer
// cchField - size of output buffer
//-----------------------------------------------------------------------------
void ConvertFieldToText( EGCSQLType eFieldType, uint8 *pubRecord, int cubRecord, char *rgchField, int cchField, bool bQuoteString )
{
char rgchTmp[k_cMedBuff];
switch ( eFieldType )
{
case k_EGCSQLType_int8:
Q_snprintf( rgchField, cchField, "%d", *( (byte *) pubRecord ) );
break;
case k_EGCSQLType_int16:
Q_snprintf( rgchField, cchField, "%d", *( (short *) pubRecord ) );
break;
case k_EGCSQLType_int32:
Q_snprintf( rgchField, cchField, "%d", *( (int *) pubRecord ) );
break;
case k_EGCSQLType_int64:
Q_snprintf( rgchField, cchField, "%lld", *( (int64 *) pubRecord ) );
break;
case k_EGCSQLType_float:
Q_snprintf( rgchField, cchField, "%f", *((float*) pubRecord) );
break;
case k_EGCSQLType_double:
Q_snprintf( rgchField, cchField, "%f", *((double*) pubRecord) );
break;
case k_EGCSQLType_String:
if ( pubRecord && *pubRecord )
{
Assert( cubRecord + 1 < Q_ARRAYSIZE( rgchTmp ) );
Q_memcpy( rgchTmp, (char *) pubRecord, cubRecord );
rgchTmp[cubRecord] = 0;
if ( bQuoteString )
{
EscapeStringValue( rgchTmp, Q_ARRAYSIZE( rgchTmp ) );
Q_snprintf( rgchField, cchField, "'%s'", rgchTmp );
}
else
{
Q_strncpy( rgchField, rgchTmp, cchField );
}
}
else
{
if ( bQuoteString )
{
Q_strncpy( rgchField, "''", cchField );
}
else
{
Q_strncpy( rgchField, "", cchField );
}
}
break;
case k_EGCSQLType_Blob:
case k_EGCSQLType_Image:
Q_strncpy( rgchField, "0x", cchField );
Q_binarytohex( pubRecord, cubRecord, rgchField + 2, cchField - 2 );
break;
default:
Assert( false );
break;
}
}
//-----------------------------------------------------------------------------
// Purpose: Returns the text SQL type for a given field
// Input: field - field to determine type for
// pchBuf - pointer to output buffer
// cchBuf - size of output buffer
// Output: returns pchBuf for convenience of one-line usage
//-----------------------------------------------------------------------------
char *SQLTypeFromField( const CColumnInfo &colInfo, char *pchBuf, int cchBuf )
{
EGCSQLType eType = colInfo.GetType();
*pchBuf = 0;
switch ( eType )
{
case k_EGCSQLType_int8:
Q_strncpy( pchBuf, "TINYINT", cchBuf );
break;
case k_EGCSQLType_int16:
Q_strncpy( pchBuf, "SMALLINT", cchBuf );
break;
case k_EGCSQLType_int32:
Q_strncpy( pchBuf, "INT", cchBuf );
break;
case k_EGCSQLType_int64:
Q_strncpy( pchBuf, "BIGINT", cchBuf );
break;
case k_EGCSQLType_float:
Q_strncpy( pchBuf, "REAL", cchBuf );
break;
case k_EGCSQLType_double:
Q_strncpy( pchBuf, "FLOAT", cchBuf );
break;
case k_EGCSQLType_String:
Q_snprintf( pchBuf, cchBuf, "VARCHAR(%d)", colInfo.GetMaxSize() );
break;
case k_EGCSQLType_Blob:
Q_snprintf( pchBuf, cchBuf, "VARBINARY(%d)", colInfo.GetMaxSize() );
break;
case k_EGCSQLType_Image:
Q_strncpy( pchBuf, "IMAGE", cchBuf );
break;
default:
Assert( false );
break;
}
return pchBuf;
}
//-----------------------------------------------------------------------------
// Purpose: Escapes any single quotes to a string value to double single quotes
// Input: rgchField - text to escape
// cchField - size of text buffer
// Notes: The text will be escaped and expanded in place in the buffer.
// In the worst case, the text may expand by 2x. (If the field is all
// single quotes.) So, you must pass in a buffer which is at least
// twice as long as the text length so we can guarantee to be able to
// escape the string.
//-----------------------------------------------------------------------------
void EscapeStringValue( char *rgchField, int cchField )
{
// TODO - what else do we need to escape? %() ...
char *pubCur = rgchField;
int nLen = 0;
int cSingleQuotes = 0;
// This function gets called on every text field we write but most text fields
// don't need to be escaped, so try to be as fast as possible in the normal case.
// first, walk through the string and count the string length and number of single quotes
while ( *pubCur )
{
if ( '\'' == *pubCur )
cSingleQuotes++;
nLen ++;
pubCur++;
}
// if no single quotes, nothing to do
if ( !cSingleQuotes )
return;
// caller must pass in a buffer that's long enough for expansion
Assert( nLen + cSingleQuotes + 1 <= cchField );
if ( !( nLen + cSingleQuotes + 1 <= cchField ) )
return;
// We know exactly how many characters the string will expand by (the # of single quotes). Walk backward
// and copy the characters into the right places. This touches each character only once.
pubCur = rgchField + nLen + cSingleQuotes;
*pubCur = 0;
pubCur--;
while ( pubCur > rgchField && cSingleQuotes > 0 )
{
// read pointer is offset from write pointer by # of remaining single quotes
char *pubRead = pubCur - cSingleQuotes;
Assert( pubRead >= rgchField );
// copy each character
*pubCur = *pubRead;
if ( '\'' == *pubRead )
{
// if the character is a single quote, back up one more and insert another single quote to escape it
pubCur --;
*pubCur = '\'';
// decrement # of single quotes remaining
cSingleQuotes --;
Assert( cSingleQuotes >= 0 );
}
pubCur--;
}
}
//-----------------------------------------------------------------------------
// Purpose: Adds constraint information to a SQL command to add or remove constraint
// Input: pchTableName - name of table
// pchColumnName - name of column
// nColFlagConstraint - flag with which constraint to
// bForAdd - whether constraint is being added or removed
// pchCmd - buffer to append SQL command to
// cchCmd - size of buffer
//-----------------------------------------------------------------------------
void AppendConstraint( const char *pchTableName, const char *pchColumnName, int nColFlagConstraint, bool bForAdd,
bool bClustered, CFmtStrMax & sCmd, int nFillFactor )
{
Assert( pchTableName && pchTableName[0] );
Assert( pchColumnName && pchColumnName[0] );
switch ( nColFlagConstraint )
{
case k_nColFlagPrimaryKey:
sCmd.AppendFormat( " CONSTRAINT %s_%s_PrimaryKey", pchTableName, pchColumnName);
if ( bForAdd )
{
sCmd += " PRIMARY KEY ";
if ( bClustered )
{
sCmd.AppendFormat( " CLUSTERED WITH (FILLFACTOR = %d) ", nFillFactor );
}
else
{
sCmd += "NONCLUSTERED";
}
}
break;
case k_nColFlagUnique:
/* do nothing - the uniqueness will be handled by creation of an index */
break;
case k_nColFlagAutoIncrement:
sCmd += " IDENTITY";
break;
default:
AssertMsg( false, "CSQLThread::AppendContraint: invalid constraint type" );
break;
}
}
//-----------------------------------------------------------------------------
// Purpose: Adds constraint information to a SQL command to add or remove constraint
// Input: pRecordInfo - record info describing table
// pColumnInfo - record info describing column
// bForAdd - whether constraint is being added or removed
// pchCmd - buffer to append SQL command to
// cchCmd - size of buffer
//-----------------------------------------------------------------------------
void AppendConstraints( const CRecordInfo *pRecordInfo, const CColumnInfo *pColumnInfo, bool bForAdd, CFmtStrMax & sCmd )
{
Assert( pRecordInfo != NULL );
Assert( pColumnInfo != NULL );
if ( pColumnInfo->BIsPrimaryKey() )
{
// any column in a PK can't be NULL.
if ( bForAdd )
{
sCmd += " NOT NULL";
}
// only add primary key constraint here if it is a single-column PK
if ( pRecordInfo->GetPrimaryKeyType() == k_EPrimaryKeyTypeSingle )
{
// get the fields on the primary key
const CUtlVector< FieldSet_t > &refFields = pRecordInfo->GetIndexFields( );
int nFillFactor = refFields.Element( pRecordInfo->GetPKIndex() ).GetFillFactor();
AppendConstraint( pRecordInfo->GetName(), pColumnInfo->GetName(), k_nColFlagPrimaryKey, bForAdd, pColumnInfo->BIsClustered(), sCmd, nFillFactor );
}
}
else if ( pColumnInfo->BIsUnique() )
{
AppendConstraint( pRecordInfo->GetName(), pColumnInfo->GetName(), k_nColFlagUnique, bForAdd, pColumnInfo->BIsClustered(), sCmd, 0 );
}
if ( pColumnInfo->BIsAutoIncrement() )
{
AppendConstraint( pRecordInfo->GetName(), pColumnInfo->GetName(), k_nColFlagAutoIncrement, bForAdd, pColumnInfo->BIsClustered(), sCmd, 0 );
}
}
//-----------------------------------------------------------------------------
// Purpose: Generates the "CONSTRAINT ..." text for the table primary key
//-----------------------------------------------------------------------------
void BuildTablePKConstraintText( TSQLCmdStr *psStatement, CRecordInfo *pRecordInfo )
{
const FieldSet_t& vecFields = pRecordInfo->GetPKFields( );
psStatement->sprintf( "CONSTRAINT %s_PrimaryKey PRIMARY KEY %s ( ",
pRecordInfo->GetName(),
vecFields.IsClustered() ? "CLUSTERED" : "NONCLUSTERED" );
for ( int nField = 0; nField < vecFields.GetCount(); nField++ )
{
// what field is the next column in our index?
int nThisField = vecFields.GetField( nField );
const CColumnInfo& columnInfo = pRecordInfo->GetColumnInfo(nThisField);
if (nField != 0)
{
*psStatement += ", ";
}
*psStatement += columnInfo.GetName();
}
// close our list
*psStatement += ") ";
if ( vecFields.GetFillFactor() != 0 )
{
// non-default fill factor, so specify it
psStatement->AppendFormat( " WITH FILLFACTOR = %d ",
vecFields.GetFillFactor() );
}
}
//-----------------------------------------------------------------------------
// Purpose: Adds constraint information to a SQL command to add or remove table-level constraints
// Input: pRecordInfo - record info describing table
// pchCmd - buffer to append SQL command to
// cchCmd - size of buffer
//-----------------------------------------------------------------------------
void AppendTableConstraints( CRecordInfo *pRecordInfo, CFmtStrMax & sCmd )
{
// the only supported table constraint is for PKs or FKs
if ( pRecordInfo->GetPrimaryKeyType() == k_EPrimaryKeyTypeMulti )
{
TSQLCmdStr tmp;
BuildTablePKConstraintText( &tmp, pRecordInfo );
sCmd += ", ";
sCmd += tmp;
}
// Look for FKs required on this table
// the only supported table constraint is for PKs or FKs
int cFKs = pRecordInfo->GetFKCount();
for( int i=0; i < cFKs; ++i )
{
FKData_t &fkData = pRecordInfo->GetFKData( i );
CFmtStr sColumns, sParentColumns;
FOR_EACH_VEC( fkData.m_VecColumnRelations, nCol )
{
FKColumnRelation_t &colRelation = fkData.m_VecColumnRelations[nCol];
if ( nCol > 0)
{
sColumns += ",";
sParentColumns += ",";
}
sColumns += colRelation.m_rgchCol;
sParentColumns += colRelation.m_rgchParentCol;
}
TSQLCmdStr sTmp;
sTmp.sprintf( ", CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s(%s) ON DELETE %s ON UPDATE %s",
fkData.m_rgchName, sColumns.Access(), fkData.m_rgchParentTableName, sParentColumns.Access(),
PchNameFromEForeignKeyAction( fkData.m_eOnDeleteAction ), PchNameFromEForeignKeyAction( fkData.m_eOnUpdateAction ) );
// add to the command
sCmd += sTmp;
}
}
//-----------------------------------------------------------------------------
// Purpose: Builds a SQL INSERT statement
// Input: psStatement - The string to put the statement into
// pRecordInfo - record info describing table inserting into
//-----------------------------------------------------------------------------
void BuildInsertStatementText( TSQLCmdStr *psStatement, const CRecordInfo *pRecordInfo )
{
psStatement->sprintf("INSERT INTO %s.%s (", GSchemaFull().GetDefaultSchemaNameForCatalog( pRecordInfo->GetESchemaCatalog() ), pRecordInfo->GetName() );
// build a string of the field names
int cColumns = pRecordInfo->GetNumColumns();
int nInsertable = 0;
bool bAddedBefore = false;
for ( int iColumn = 0; iColumn < cColumns; iColumn++ )
{
const CColumnInfo &columnInfo = pRecordInfo->GetColumnInfo( iColumn );
if ( !columnInfo.BIsInsertable() )
continue;
nInsertable++;
if ( bAddedBefore )
psStatement->Append( ',' );
bAddedBefore = true;
psStatement->Append( columnInfo.GetName() );
}
psStatement->AppendFormat( ") VALUES (%.*s)", GetInsertArgStringChars( nInsertable ), GetInsertArgString() );
}
//-----------------------------------------------------------------------------
// Purpose: Builds a SQL INSERT statement
// IMPORTANT NOTE - This Insert statement will use the Microsoft SQL Server
// specific clause 'OUTPUT Inserted.ColumnName'
// The result of that will be that the SQL statement will return to us
// the columns that could not be specified by the Insert.
// At the time of writing, that is primarily AutoIncrement columns,
// however in theory we should be able to recover any computed column
// from SQL server, with the caveats specified at :
// http://msdn.microsoft.com/en-us/library/ms177564.aspx
//
// Input: psStatement - The output statement string
// pRecordInfo - record info describing table inserting into
//-----------------------------------------------------------------------------
void BuildInsertAndReadStatementText( TSQLCmdStr *psStatement, CUtlVector<int> *pvecOutputFields, const CRecordInfo *pRecordInfo )
{
psStatement->sprintf("INSERT INTO %s.%s (", GSchemaFull().GetDefaultSchemaNameForCatalog( pRecordInfo->GetESchemaCatalog() ), pRecordInfo->GetName() );
// build a string of the field names
int nInsertable = 0;
int cColumns = pRecordInfo->GetNumColumns();
bool bAddedBefore = false;
for ( int iColumn = 0; iColumn < cColumns; iColumn++ )
{
const CColumnInfo &columnInfo = pRecordInfo->GetColumnInfo( iColumn );
if ( !columnInfo.BIsInsertable() )
continue;
nInsertable++;
if ( bAddedBefore )
psStatement->Append( ',' );
bAddedBefore = true;
psStatement->Append( columnInfo.GetName() );
}
bAddedBefore = false ;
int nOutputColumn = 0;
for( int iColumn = 0; iColumn < cColumns; iColumn++ )
{
const CColumnInfo &columnInfo = pRecordInfo->GetColumnInfo( iColumn ) ;
//
// If we can't Insert it - we want SQL Server to tell us what value was stored
// in the column !!
//
if( !columnInfo.BIsInsertable() )
{
if( bAddedBefore )
psStatement->Append( ", INSERTED." );
else
psStatement->Append( ") OUTPUT INSERTED." );
bAddedBefore = true ;
psStatement->Append( columnInfo.GetName() );
pvecOutputFields->AddToTail( iColumn );
nOutputColumn++;
}
}
// add field values to SQL statement
psStatement->AppendFormat( " VALUES (%.*s)", GetInsertArgStringChars( nInsertable ), GetInsertArgString() );
}
//-----------------------------------------------------------------------------
// Purpose: Builds a SQL MERGE statement update or insert using in-flight values table
// Input: psStatement - The string to put the statement into
// pRecordInfo - record info describing table inserting into
//-----------------------------------------------------------------------------
void BuildMergeStatementTextOnPKWhenMatchedUpdateWhenNotMatchedInsert( TSQLCmdStr *psStatement, const CRecordInfo *pRecordInfo )
{
psStatement->sprintf( "MERGE INTO %s.%s WITH( HOLDLOCK, ROWLOCK ) T USING ( VALUES (%.*s) ) AS S(",
GSchemaFull().GetDefaultSchemaNameForCatalog( pRecordInfo->GetESchemaCatalog() ), pRecordInfo->GetName(),
GetInsertArgStringChars( pRecordInfo->GetNumColumns() ), GetInsertArgString() );
{
int cColumns = pRecordInfo->GetNumColumns();
for ( int iColumn = 0; iColumn < cColumns; iColumn++ )
{
const CColumnInfo &columnInfo = pRecordInfo->GetColumnInfo( iColumn );
if ( iColumn )
psStatement->Append( ',' );
psStatement->Append( columnInfo.GetName() );
}
}
psStatement->Append( ") ON " );
// build a string of the PK columns
const FieldSet_t &fsPK = pRecordInfo->GetIndexFields()[pRecordInfo->GetPKIndex()];
{
int cColumns = fsPK.GetCount();
for ( int iColumn = 0; iColumn < cColumns; iColumn++ )
{
const CColumnInfo &columnInfo = pRecordInfo->GetColumnInfo( fsPK.GetField( iColumn ) );
if ( iColumn )
psStatement->Append( " AND " );
psStatement->Append( "T." );
psStatement->Append( columnInfo.GetName() );
psStatement->Append( "=S." );
psStatement->Append( columnInfo.GetName() );
}
}
psStatement->Append( " WHEN MATCHED THEN UPDATE SET " );
// build the update string
{
int cColumns = pRecordInfo->GetNumColumns();
bool bAddedBefore = false;
for ( int iColumn = 0; iColumn < cColumns; iColumn++ )
{
bool bThisColumnIsPartOfPK = false;
for ( int ipkCheck = 0; ipkCheck < fsPK.GetCount(); ++ipkCheck )
{
if ( iColumn == fsPK.GetField( ipkCheck ) )
{
bThisColumnIsPartOfPK = true;
break;
}
}
if ( bThisColumnIsPartOfPK )
continue;
const CColumnInfo &columnInfo = pRecordInfo->GetColumnInfo( iColumn );
if ( bAddedBefore )
psStatement->Append( ',' );
bAddedBefore = true;
psStatement->Append( columnInfo.GetName() );
psStatement->Append( "=S." );
psStatement->Append( columnInfo.GetName() );
}
}
psStatement->Append( " WHEN NOT MATCHED BY TARGET THEN INSERT (" );
// build a string of the field names
{
int cColumns = pRecordInfo->GetNumColumns();
bool bAddedBefore = false;
for ( int iColumn = 0; iColumn < cColumns; iColumn++ )
{
const CColumnInfo &columnInfo = pRecordInfo->GetColumnInfo( iColumn );
if ( !columnInfo.BIsInsertable() )
continue;
if ( bAddedBefore )
psStatement->Append( ',' );
bAddedBefore = true;
psStatement->Append( columnInfo.GetName() );
}
}
psStatement->Append( ") VALUES (" );
{
int cColumns = pRecordInfo->GetNumColumns();
bool bAddedBefore = false;
for ( int iColumn = 0; iColumn < cColumns; iColumn++ )
{
const CColumnInfo &columnInfo = pRecordInfo->GetColumnInfo( iColumn );
if ( !columnInfo.BIsInsertable() )
continue;
if ( bAddedBefore )
psStatement->Append( ',' );
bAddedBefore = true;
psStatement->Append( "S." );
psStatement->Append( columnInfo.GetName() );
}
}
psStatement->Append( ");" );
}
//-----------------------------------------------------------------------------
// Purpose: Builds a SQL MERGE statement using CTE_MergeParams as supplied table holding rows
// Input: psStatement - The string to put the statement into
// pRecordInfo - record info describing table inserting into
//-----------------------------------------------------------------------------
void BuildMergeStatementTextOnPKWhenNotMatchedInsert( TSQLCmdStr *psStatement, const CRecordInfo *pRecordInfo )
{
psStatement->sprintf( "MERGE INTO %s.%s WITH( HOLDLOCK, ROWLOCK ) T USING ( VALUES (%.*s) ) AS S(",
GSchemaFull().GetDefaultSchemaNameForCatalog( pRecordInfo->GetESchemaCatalog() ), pRecordInfo->GetName(),
GetInsertArgStringChars( pRecordInfo->GetNumColumns() ), GetInsertArgString() );
{
int cColumns = pRecordInfo->GetNumColumns();
for ( int iColumn = 0; iColumn < cColumns; iColumn++ )
{
const CColumnInfo &columnInfo = pRecordInfo->GetColumnInfo( iColumn );
if ( iColumn )
psStatement->Append( ',' );
psStatement->Append( columnInfo.GetName() );
}
}
psStatement->Append( ") ON " );
// build a string of the PK columns
const FieldSet_t &fsPK = pRecordInfo->GetIndexFields()[pRecordInfo->GetPKIndex()];
{
int cColumns = fsPK.GetCount();
for ( int iColumn = 0; iColumn < cColumns; iColumn++ )
{
const CColumnInfo &columnInfo = pRecordInfo->GetColumnInfo( fsPK.GetField( iColumn ) );
if ( iColumn )
psStatement->Append( " AND " );
psStatement->Append( "T." );
psStatement->Append( columnInfo.GetName() );
psStatement->Append( "=S." );
psStatement->Append( columnInfo.GetName() );
}
}
psStatement->Append( " WHEN NOT MATCHED BY TARGET THEN INSERT (" );
// build a string of the field names
{
int cColumns = pRecordInfo->GetNumColumns();
bool bAddedBefore = false;
for ( int iColumn = 0; iColumn < cColumns; iColumn++ )
{
const CColumnInfo &columnInfo = pRecordInfo->GetColumnInfo( iColumn );
if ( !columnInfo.BIsInsertable() )
continue;
if ( bAddedBefore )
psStatement->Append( ',' );
bAddedBefore = true;
psStatement->Append( columnInfo.GetName() );
}
}
psStatement->Append( ") VALUES (" );
{
int cColumns = pRecordInfo->GetNumColumns();
bool bAddedBefore = false;
for ( int iColumn = 0; iColumn < cColumns; iColumn++ )
{
const CColumnInfo &columnInfo = pRecordInfo->GetColumnInfo( iColumn );
if ( !columnInfo.BIsInsertable() )
continue;
if ( bAddedBefore )
psStatement->Append( ',' );
bAddedBefore = true;
psStatement->Append( "S." );
psStatement->Append( columnInfo.GetName() );
}
}
psStatement->Append( ");" );
}
void BuildSelectStatementText( TSQLCmdStr *psStatement, const CColumnSet & selectSet, const char *pchTopClause )
{
*psStatement = "SELECT ";
if( pchTopClause )
{
psStatement->Append( pchTopClause );
psStatement->Append( ' ' );
}
// build a string of the field names
bool bAddedBefore = false;
FOR_EACH_COLUMN_IN_SET( selectSet, nColumnIndex )
{
const CColumnInfo &columnInfo = selectSet.GetColumnInfo( nColumnIndex );
if ( bAddedBefore )
psStatement->Append( ',' );
bAddedBefore = true;
psStatement->Append( columnInfo.GetName() );
}
psStatement->Append( " FROM ");
psStatement->Append( GSchemaFull().GetDefaultSchemaNameForCatalog( selectSet.GetRecordInfo()->GetESchemaCatalog() ) );
psStatement->Append( '.' );
psStatement->Append( selectSet.GetRecordInfo()->GetName() );
}
//-----------------------------------------------------------------------------
// Purpose: Builds a SQL UPDATE statement
// Input: pRecordInfo - record info describing table inserting into
// bForPreparedStatement - if true, inserts values as '?' for later
// binding. If false, values are inserted in text.
// pchStatement - pointer to buffer to build statement in
// cchStatement - size of buffer
// pSQLRecord - pointer to record with data to update
// iColumnMatch - column to use for WHERE condition
// pvMatch - data value to use for WHERE condition
// cubMatch - size of pvMatch data
// rgiColumnUpdate - array of column #'s to update
// ciColumnUpdate - count of column #'s to update
//-----------------------------------------------------------------------------
void BuildUpdateStatementText( TSQLCmdStr *psStatement, const CColumnSet & updateColumns )
{
// build the UPDATE statement
psStatement->sprintf( "UPDATE %s.%s SET ", GSchemaFull().GetDefaultSchemaNameForCatalog( updateColumns.GetRecordInfo()->GetESchemaCatalog() ), updateColumns.GetRecordInfo()->GetName() );
// add each field we're updating to the UPDATE statement
FOR_EACH_COLUMN_IN_SET( updateColumns, nColumnIndex )
{
const CColumnInfo &columnInfo = updateColumns.GetColumnInfo( nColumnIndex );
if( nColumnIndex > 0 )
psStatement->Append( ',' );
psStatement->Append( columnInfo.GetName() );
psStatement->Append( "=?" );
}
}
//-----------------------------------------------------------------------------
// Purpose: Builds a SQL UPDATE statement
//-----------------------------------------------------------------------------
void BuildDeleteStatementText( TSQLCmdStr *psStatement, const CRecordInfo *pRecordInfo )
{
psStatement->sprintf( "DELETE FROM %s.%s", GSchemaFull().GetDefaultSchemaNameForCatalog( pRecordInfo->GetESchemaCatalog() ), pRecordInfo->GetName() );
}
//-----------------------------------------------------------------------------
// Purpose: Builds a where clause for the provided fields
//-----------------------------------------------------------------------------
void AppendWhereClauseText( TSQLCmdStr *psClause, const CColumnSet & columnSet )
{
// add each field we're updating to the UPDATE statement
FOR_EACH_COLUMN_IN_SET( columnSet, nColumnIndex )
{
const CColumnInfo &columnInfo = columnSet.GetColumnInfo( nColumnIndex );
if( nColumnIndex > 0 )
psClause->Append( " AND ");
psClause->Append( columnInfo.GetName() );
psClause->Append( "=?" );
}
}
//-----------------------------------------------------------------------------
// Purpose: Builds an OUTPUT [fields] INTO [table] for the provided fields/data
//-----------------------------------------------------------------------------
void BuildOutputClauseText( TSQLCmdStr *psClause, const CColumnSet & columnSet )
{
*psClause = " OUTPUT ";
FOR_EACH_COLUMN_IN_SET( columnSet, nColumnIndex )
{
const CColumnInfo &columnInfo = columnSet.GetColumnInfo( nColumnIndex );
if( nColumnIndex > 0 )
psClause->Append( ", ");
psClause->Append( " ? AS " );
psClause->Append( columnInfo.GetName() );
}
psClause->Append( " INTO " );
psClause->Append( columnSet.GetRecordInfo()->GetName() );
}
////-----------------------------------------------------------------------------
//// Purpose: our own special "upsert" into a column with a uniqueness constraint
////-----------------------------------------------------------------------------
//EResult UpdateOrInsertUnique( CSQLAccess &sqlAccess, int iTable, int iField, CRecordBase *pRecordBase, int iIndexID )
//{
// // attempt an update - if it fails due to duplicate primary key, they can't use this
// // url (it's taken) - if it succeeds but affects 0 rows, they didn't have a vanity url
// // and we need to do an insert (which could again fail due to primary key constraints)
// int cRecordsUpdated = 0;
// bool bRet = sqlAccess.BYieldingUpdateFieldFromRecordWithIndex( iTable, &cRecordsUpdated, iField, pRecordBase, iIndexID );
// if ( !bRet )
// {
// // ODBC is the suck - give me Spring JDBC templates, please.
// if ( sqlAccess.GetLastError()->IsDuplicateInsertAttempt() )
// {
// return k_EResultDuplicateName;
// }
// return k_EResultFail;
// }
// else if ( 0 == cRecordsUpdated )
// {
// // the user didn't have an entry, so insert one.
// bRet = sqlAccess.BYieldingInsertRecord( iTable, pRecordBase );
// if ( !bRet )
// {
// // ODBC is the suck - give me Spring JDBC templates, please.
// if ( sqlAccess.GetLastError()->IsDuplicateInsertAttempt() )
// {
// return k_EResultDuplicateName;
// }
// return k_EResultFail;
// }
// }
// return k_EResultOK;
//}
//
} // namespace GCSDK