You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
915 lines
26 KiB
915 lines
26 KiB
//========= 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
|
|
|