mirror of
https://github.com/nillerusr/source-engine.git
synced 2026-08-07 17:29:36 +00:00
1
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user