diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /public/gcsdk/sqlaccess | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'public/gcsdk/sqlaccess')
| -rw-r--r-- | public/gcsdk/sqlaccess/columnset.h | 219 | ||||
| -rw-r--r-- | public/gcsdk/sqlaccess/record.h | 467 | ||||
| -rw-r--r-- | public/gcsdk/sqlaccess/recordinfo.h | 174 | ||||
| -rw-r--r-- | public/gcsdk/sqlaccess/schema.h | 601 | ||||
| -rw-r--r-- | public/gcsdk/sqlaccess/schemafull.h | 284 | ||||
| -rw-r--r-- | public/gcsdk/sqlaccess/schemaupdate.h | 95 | ||||
| -rw-r--r-- | public/gcsdk/sqlaccess/sqlaccess.h | 290 | ||||
| -rw-r--r-- | public/gcsdk/sqlaccess/sqlrecord.h | 54 | ||||
| -rw-r--r-- | public/gcsdk/sqlaccess/sqlutil.h | 113 |
9 files changed, 2297 insertions, 0 deletions
diff --git a/public/gcsdk/sqlaccess/columnset.h b/public/gcsdk/sqlaccess/columnset.h new file mode 100644 index 0000000..f61aa5b --- /dev/null +++ b/public/gcsdk/sqlaccess/columnset.h @@ -0,0 +1,219 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Sets of columns in queries +// +// $NoKeywords: $ +//============================================================================= + +#ifndef COLUMNSET_H +#define COLUMNSET_H + +#ifdef _WIN32 +#pragma once +#endif + +namespace GCSDK +{ + +//----------------------------------------------------------------------------- +// Purpose: Sets of columns in queries +//----------------------------------------------------------------------------- +class CColumnSet +{ +public: + CColumnSet( const CRecordInfo *pRecordInfo ); + CColumnSet( const CColumnSet & rhs ); + CColumnSet & operator=( const CColumnSet & rhs ); + const CColumnSet operator+( const CColumnSet & rhs ) const; + CColumnSet & operator+=( const CColumnSet & rhs ); + + //NOTE: These do not ensure uniqueness, the CSET_ macros below should be used instead as they will compile time enforce uniqueness + CColumnSet( const CRecordInfo *pRecordInfo, int col1 ); + CColumnSet( const CRecordInfo *pRecordInfo, int col1, int col2 ); + CColumnSet( const CRecordInfo *pRecordInfo, int col1, int col2, int col3 ); + CColumnSet( const CRecordInfo *pRecordInfo, int col1, int col2, int col3, int col4 ); + CColumnSet( const CRecordInfo *pRecordInfo, int col1, int col2, int col3, int col4, int col5 ); + CColumnSet( const CRecordInfo *pRecordInfo, int col1, int col2, int col3, int col4, int col5, int col6 ); + CColumnSet( const CRecordInfo *pRecordInfo, int col1, int col2, int col3, int col4, int col5, int col6, int col7 ); + CColumnSet( const CRecordInfo *pRecordInfo, int col1, int col2, int col3, int col4, int col5, int col6, int col7, int col8 ); + + void BAddColumn( int nColumn ); + void BRemoveColumn( int nColumn ); + bool IsSet( int nColumn ) const; + bool IsEmpty() const { return m_vecColumns.Count() == 0;} + + uint32 GetColumnCount() const; + int GetColumn( int nIndex ) const; + const CColumnInfo & GetColumnInfo( int nIndex ) const; + + const CRecordInfo *GetRecordInfo() const { return m_pRecordInfo; } + + void MakeEmpty(); + void MakeFull(); + void MakeInsertable(); + void MakeNoninsertable(); + void MakePrimaryKey(); + void MakeInverse( const CColumnSet & columnSet ); + //determines if the current column set has all fields set. Useful for detection of new columns being added to the schema + bool BAreAllFieldsSet() const; + + template< typename TSchClass > + static CColumnSet Empty(); + template< typename TSchClass > + static CColumnSet Full(); + template< typename TSchClass > + static CColumnSet Insertable(); + template< typename TSchClass > + static CColumnSet Noninsertable(); + template< typename TSchClass > + static CColumnSet PrimaryKey(); + static CColumnSet Inverse( const CColumnSet & columnSet ); + +#ifdef DBGFLAG_VALIDATE + void Validate( CValidator &validator, const char *pchName ); +#endif + +private: + CUtlVector<int> m_vecColumns; + const CRecordInfo *m_pRecordInfo; +}; + +//this is a utility class which can at compile time ensure that all of the provided column values are unique and will generate an error if that doesn't +//hold true. The default values just need to be unique and greater than what is expected for real columns +template < int n1 = 10001, int n2 = 10002, int n3 = 10003, int n4 = 10004, int n5 = 10005, int n6 = 10006, int n7 = 10007, int n8 = 10008 > +class CUniqueColChecker +{ +public: + + //this is a simple pass through so that it can wrap the declaration of a column set + static const CColumnSet& VerifyUnique( const CColumnSet& cs ) + { + COMPILE_TIME_ASSERT( n1 != n2 && n1 != n3 && n1 != n4 && n1 != n5 && n1 != n6 && n1 != n7 && n1 != n8 ); + COMPILE_TIME_ASSERT( n2 != n3 && n2 != n4 && n2 != n5 && n2 != n6 && n2 != n7 && n2 != n8 ); + COMPILE_TIME_ASSERT( n3 != n4 && n3 != n5 && n3 != n6 && n3 != n7 && n3 != n8 ); + COMPILE_TIME_ASSERT( n4 != n5 && n4 != n6 && n4 != n7 && n4 != n8 ); + COMPILE_TIME_ASSERT( n5 != n6 && n5 != n7 && n5 != n8 ); + COMPILE_TIME_ASSERT( n6 != n7 && n6 != n8 ); + COMPILE_TIME_ASSERT( n7 != n8 ); + return cs; + } +}; + +// Usage notes: +// The fields in a column set are order-dependent, and must match the order of the fields in +// the query used to generate the data. The code that reads values doesn't do any fancy +// name-matching and will copy values to incorrect locations silently if there is a +// disagreement between the fields in the query and the fields in the column set. +// +// Examples: +// // This is broken. +// query = "SELECT * FROM Items"; +// columnSet = CSET_12_COL( CSchItem, individual_field_names ); +// +// // This is fixed. +// query = "SELECT * FROM Items"; +// columnSet = CSET_FULL( ... ); + +#define FOR_EACH_COLUMN_IN_SET( columnSet, iterName ) for( uint32 iterName = 0; iterName < (columnSet).GetColumnCount(); iterName++ ) + +#define CSET_EMPTY( schClass ) CColumnSet::Empty<schClass>() +#define CSET_FULL( schClass ) CColumnSet::Full<schClass>() +#define CSET_INSERTABLE( schClass ) CColumnSet::Insertable<schClass>() +#define CSET_NONINSERTABLE( schClass ) CColumnSet::Noninsertable<schClass>() +#define CSET_PK( schClass ) CColumnSet::PrimaryKey<schClass>() + +#define CSET_1_COL( schClass, col1 ) \ + CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col1 ) + +#define CSET_2_COL( schClass, col1, col2 ) \ + CUniqueColChecker< schClass::col1, schClass::col2 >::VerifyUnique( \ + CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col1, schClass::col2 ) ) + +#define CSET_3_COL( schClass, col1, col2, col3 ) \ + CUniqueColChecker< schClass::col1, schClass::col2, schClass::col3 >::VerifyUnique( \ + CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col1, schClass::col2, schClass::col3 ) ) + +#define CSET_4_COL( schClass, col1, col2, col3, col4 ) \ + CUniqueColChecker< schClass::col1, schClass::col2, schClass::col3, schClass::col4 >::VerifyUnique( \ + CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col1, schClass::col2, schClass::col3, schClass::col4 ) ) + +#define CSET_5_COL( schClass, col1, col2, col3, col4, col5 ) \ + CUniqueColChecker< schClass::col1, schClass::col2, schClass::col3, schClass::col4, schClass::col5 >::VerifyUnique( \ + CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col1, schClass::col2, schClass::col3, schClass::col4, schClass::col5 ) ) + +#define CSET_6_COL( schClass, col1, col2, col3, col4, col5, col6 ) \ + CUniqueColChecker< schClass::col1, schClass::col2, schClass::col3, schClass::col4, schClass::col5, schClass::col6 >::VerifyUnique( \ + CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col1, schClass::col2, schClass::col3, schClass::col4, schClass::col5, schClass::col6 ) ) + +#define CSET_7_COL( schClass, col1, col2, col3, col4, col5, col6, col7 ) \ + CUniqueColChecker< schClass::col1, schClass::col2, schClass::col3, schClass::col4, schClass::col5, schClass::col6, schClass::col7 >::VerifyUnique( \ + CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col1, schClass::col2, schClass::col3, schClass::col4, schClass::col5, schClass::col6, schClass::col7 ) ) + +#define CSET_8_COL( schClass, col1, col2, col3, col4, col5, col6, col7, col8 ) \ + CUniqueColChecker< schClass::col1, schClass::col2, schClass::col3, schClass::col4, schClass::col5, schClass::col6, schClass::col7, schClass::col8 >::VerifyUnique( \ + CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col1, schClass::col2, schClass::col3, schClass::col4, schClass::col5, schClass::col6, schClass::col7, schClass::col8 ) ) + + +//----------------------------------------------------------------------------- +// Purpose: Returns an empty Column Set for a schema class +//----------------------------------------------------------------------------- +template< typename TSchClass > +CColumnSet CColumnSet::Empty() +{ + CColumnSet set( GSchemaFull().GetSchema( TSchClass::k_iTable ).GetRecordInfo() ); + return set; +} + + +//----------------------------------------------------------------------------- +// Purpose: Returns a Column Set for a schema class which contains every field +//----------------------------------------------------------------------------- +template< typename TSchClass > +CColumnSet CColumnSet::Full() +{ + CColumnSet set( GSchemaFull().GetSchema( TSchClass::k_iTable ).GetRecordInfo() ); + set.MakeFull(); + return set; +} + + +//----------------------------------------------------------------------------- +// Purpose: Returns a Column Set for a schema class which contains every +// insertable field +//----------------------------------------------------------------------------- +template< typename TSchClass > +CColumnSet CColumnSet::Insertable() +{ + CColumnSet set( GSchemaFull().GetSchema( TSchClass::k_iTable ).GetRecordInfo() ); + set.MakeInsertable(); + return set; +} + + +//----------------------------------------------------------------------------- +// Purpose: Returns a Column Set for a schema class which contains every +// noninsertable field +//----------------------------------------------------------------------------- +template< typename TSchClass > +CColumnSet CColumnSet::Noninsertable() +{ + CColumnSet set( GSchemaFull().GetSchema( TSchClass::k_iTable ).GetRecordInfo() ); + set.MakeNoninsertable(); + return set; +} + + +//----------------------------------------------------------------------------- +// Purpose: Returns a Column Set for a schema class which contains every +// primary key field +//----------------------------------------------------------------------------- +template< typename TSchClass > +CColumnSet CColumnSet::PrimaryKey() +{ + CColumnSet set( GSchemaFull().GetSchema( TSchClass::k_iTable ).GetRecordInfo() ); + set.MakePrimaryKey(); + return set; +} + +} // namespace GCSDK +#endif // COLUMNSET_H diff --git a/public/gcsdk/sqlaccess/record.h b/public/gcsdk/sqlaccess/record.h new file mode 100644 index 0000000..38dd507 --- /dev/null +++ b/public/gcsdk/sqlaccess/record.h @@ -0,0 +1,467 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//============================================================================= + +#ifndef GCRECORD_H +#define GCRECORD_H +#ifdef _WIN32 +#pragma once +#endif + +#include "schema.h" + +#include "tier0/memdbgon.h" +namespace GCSDK +{ +#pragma pack( push, 1 ) + +class CRecordInfo; + +//----------------------------------------------------------------------------- +// VarFieldBlockInfo_t +// Tracks a block of memory used to hold all the variable-length +// fields for a record. +//----------------------------------------------------------------------------- +struct VarFieldBlockInfo_t +{ + union + { + // Take up 64-bits of space now even though + // pointers are still 32 bits + uint8* m_pubBlock; + uint64 _unused; + }; + uint32 m_cubBlock; // how much is in this block? + uint32 m_cubBlockFree; // how much in this block is free? +}; + +//----------------------------------------------------------------------------- +// VarField_t +// Data format for a variable field entry in a DS record +// For leaf code, is hidden inside a CVarField or CVarCharField +//----------------------------------------------------------------------------- +struct VarField_t +{ + uint32 m_cubField; // Size of the field + uint32 m_dubOffset; // Offset of the field within the block +}; + +//----------------------------------------------------------------------------- +// CVarField +// Defines a class to encompass a variable-length field - opaque +//----------------------------------------------------------------------------- +class CVarField : private VarField_t +{ +public: + friend class CRecordVar; +private: +}; + + +//----------------------------------------------------------------------------- +// CVarCharField +// Defines a class to encompass a variable-length string field - opaque +//----------------------------------------------------------------------------- +class CVarCharField : public CVarField +{ +public: + friend class CRecordVar; +}; + + +//----------------------------------------------------------------------------- +// CVarCharField +// Defines a class to encompass a variable-length string field - opaque +//----------------------------------------------------------------------------- +class CVarBinaryField : public CVarField +{ +public: + friend class CRecordVar; +}; + + +//----------------------------------------------------------------------------- +// CImageField +// Defines a class to encompass a long variable-length binary field - opaque +//----------------------------------------------------------------------------- +class CImageField : public CVarField +{ +public: + friend class CRecordVar; +}; + + +#pragma pack( pop ) + +// fix the size of this just to be safe +#pragma pack( push, 4 ) + +class CSchema; + +//----------------------------------------------------------------------------- +// CRecordBase +// Defines a class which arbitrates access to a fixed-length record +// +// This is used as a base class for the CSchTable wrapper classes emitted +// by the schema compiler when the involved table has no variable length data. +//----------------------------------------------------------------------------- +class CRecordBase +{ +public: + // These both allocate new space and COPY the record data into it + CRecordBase( const CRecordBase &that ); + CRecordBase &operator=(const CRecordBase &that); + + // Init from general memory + int InitFromBytes( uint8 *pubRecord ); + + virtual ~CRecordBase(); + + // Use these when sending records over the wire + uint32 CubSerialized(); + + virtual uint8 *PubRecordFixed(); + const uint8 *PubRecordFixed() const; + uint32 CubRecordFixed() const; + + virtual uint8 *PubRecordVarBlock(); + virtual const uint8 *PubRecordVarBlock() const; + uint32 CubRecordVarBlock() const; + bool BAssureRecordVarStorage( uint32 cVariableBytes ); + + // generic data accessors + bool BGetField( int iField, uint8 **ppubData, uint32 *pcubField ) const; + virtual bool BSetField( int iField, void * pvData, uint32 cubData ); + virtual void WipeField( int iField ); + + // data accessors + const char * GetStringField( int iField, uint32 *pcubField ); + int GetInt( int iField ); + uint16 GetUint16( int iField ); + uint32 GetUint32( int iField ); + uint64 GetUint64( int iField ); + + // variable length data accessors + // (not implemented by this class) + virtual const char *ReadVarCharField( const CVarCharField &field ) const; + virtual const uint8 *ReadVarDataField( const CVarField &field, uint32 *pcubField ) const; + virtual bool SetVarCharField( CVarCharField &field, const char *pchString, bool bTruncate, int32 iField ); + virtual void SetVarDataField( CVarField &field, const void *pvData, uint32 cubData ); + + virtual const CSchema *GetPSchema() const + { + return const_cast<CRecordBase*>(this)->GetPSchema(); + } + virtual CSchema *GetPSchema(); + const CRecordInfo *GetPRecordInfo() const; + + // implemented by CSch-something derivatives + virtual int GetITable() const = 0; + + void RenderField( uint32 unColumn, int cchBuffer, char *pchBuffer ) const; + +#ifdef DBGFLAG_VALIDATE + void Validate( CValidator &validator, const char *pchName ); // Validate our internal structures + static void ValidateStatics( CValidator &validator, const char *pchName ); +#endif // DBGFLAG_VALIDATE + +protected: + // copies the contents of the record. The assignement operator uses this internally + virtual void Copy( const CRecordBase & that ); + + CSchema *GetPSchemaImpl(); + void Cleanup(); + bool BSetField( int iField, void *pvData, uint32 cubData, bool *pbRealloced ); + + + // ctor for derived classes, CSch* + CRecordBase( ) { } + +}; + + +//----------------------------------------------------------------------------- +// CRecordVar +// Defines a class which arbitrates access to a variable-length record +// +// This is used as a base class for the CSchTable wrapper classes emitted +// by the schema compiler when the involved table *does* have variable-length data +//----------------------------------------------------------------------------- +class CRecordVar : public CRecordBase +{ +public: + CRecordVar( ) + { + m_pSchema = NULL; + m_nFlags = 0; + } + + virtual ~CRecordVar() + { + Cleanup(); + } + + virtual uint8* PubRecordFixed(); + const uint8 *PubRecordFixed() const; + + virtual CSchema *GetPSchema() + { + return m_pSchema; + } + virtual const CSchema *GetPSchema() const + { + return m_pSchema; + } + + // Init from general memory + int InitFromBytes( uint8 *pubRecord ); + + // generic data accessors + virtual bool BSetField( int iField, void * pvData, uint32 cubData ); + virtual void WipeField( int iField ); + + // variable-length data accessors + virtual const char *ReadVarCharField( const CVarCharField &field ) const; + virtual const uint8 *ReadVarDataField( const CVarField &field, uint32 *pcubField ) const; + virtual bool SetVarCharField( CVarCharField &field, const char *pchString, bool bTruncate, int32 iField ); + virtual void SetVarDataField( CVarField &field, const void *pvData, uint32 cubData ); + + // allocated fixed means we've got our own memory for the fixed record + // allocated var block means we've allocated a block for the variable part of this record + enum EFlags { k_EAllocatedFixed = 0x1, k_EAllocatedVarBlock = 0x2 }; + bool BFlagSet( int eFlag ) const; + +#ifdef DBGFLAG_VALIDATE + void Validate( CValidator &validator, const char *pchName ); // Validate our internal structures +#endif // DBGFLAG_VALIDATE + +protected: + // copies the contents of the record. The assignement operator uses this internally + virtual void Copy( const CRecordBase & that ); + + // initialization helper + void DoInit() + { + m_pSchema = CRecordBase::GetPSchema(); + } + + void SetFlag( int eFlag, bool bSet ); + + void Cleanup(); + CSchema *m_pSchema; // Corresponding Schema + int m_nFlags; // Flags about the record memory allocations / location +}; + + +//----------------------------------------------------------------------------- +// CRecordExternal +// Defines a class which arbitrates access to a variable-length record +// +// This is used as an accessor for a polymorphic record. It can be used to +// read CSchTable records when the type is unknown, manipulate stats records, +// or touch data that isn't preallocated. Its use is relatively rare. +//----------------------------------------------------------------------------- +class CRecordExternal : public CRecordVar +{ +public: + CRecordExternal() + { + m_pubRecordFixedExternal = NULL; + m_nFlags = 0; + } + + virtual ~CRecordExternal() + { + Cleanup(); + } + + virtual uint8* PubRecordFixed(); + const uint8 *PubRecordFixed() const; + + void DeSerialize( uint8 *pubData ); + + int GetITable() const + { + return -1; + } + + void Init( CSchema *pSchema ); + int Init( CSchema *pSchema, uint8 *pubRecord, bool bTakeOwnership ); + + // test helpers + void InitRecordRandom( uint32 unPrimaryIndex ); + void SetFieldRandom( int iField ); + +#ifdef DBGFLAG_VALIDATE + void Validate( CValidator &validator, const char *pchName ); // Validate our internal structures +#endif // DBGFLAG_VALIDATE + +protected: + // copies the contents of the record. The assignement operator uses this internally + virtual void Copy( const CRecordBase & that ); + + void Cleanup(); + + void DoInit() + { + m_pSchema = CRecordBase::GetPSchema(); + } + + uint8 *m_pubRecordFixedExternal; // If the fixed record is not a part of this object, + // this points to where it is + + CSchema *GetPSchemaImpl(); +}; + + +//----------------------------------------------------------------------------- +// Accessors for variable length character data. +// These goofy little macros implement some syntax sugar +//----------------------------------------------------------------------------- + +#define READ_VAR_CHAR_FIELD( record, field )\ + (record).ReadVarCharField( (record).field ) + +#define WRITE_VAR_CHAR_FIELD( record, field, text )\ + (record).SetVarCharField( (record).m_##field, text, false, (record).k_iField_##field ) + +#define WRITE_VAR_CHAR_FIELD_TRUNC( record, field, text )\ + (record).SetVarCharField( (record).m_##field, text, true, (record).k_iField_##field ) + +#pragma pack( pop ) + + +//----------------------------------------------------------------------------- +// Template classes that get a LessFunc that sorts CRecordBases by a field +// within them +//----------------------------------------------------------------------------- +template <class T, int I, typename F> +class CDefSchOps +{ +public: + static bool LessFunc( const T &lhs, const T &rhs ) + { + // Check that the field number is valid + COMPILE_TIME_ASSERT( I >= 0 && I < T::k_iFieldMax ); + + // Check to make sure this is a fixed field + const Field_t &fieldInfo = lhs.GetPSchema()->GetField( I ); + Assert( !fieldInfo.BIsStringType() && !fieldInfo.BIsVariableLength() ); + if ( fieldInfo.BIsStringType() || fieldInfo.BIsVariableLength() ) + return false; + + // Read the data and make sure the sizes are correct for the field type we expect + uint8 *pubLhs; + uint8 *pubRhs; + bool bRet; + uint32 cubRead; + + bRet = lhs.BGetField( I, &pubLhs, &cubRead ); + Assert( bRet && cubRead == sizeof( F ) ); + if ( !bRet || cubRead != sizeof( F ) ) + return false; + + bRet = rhs.BGetField( I, &pubRhs, &cubRead ); + Assert( bRet && cubRead == sizeof( F ) ); + if ( !bRet || cubRead != sizeof( F ) ) + return false; + + // Finally do the comparison + return ( *( (F *)pubLhs ) ) < ( *( (F *)pubRhs ) ); + } + + static bool LessFuncCtx( const T &lhs, const T &rhs, void *pCtx ) + { + return LessFunc( lhs, rhs ); + } +}; + + +#define DefSchLessFunc( RecordType, FieldIndex, FieldType ) CDefSchOps<RecordType, FieldIndex, FieldType>::LessFunc +#define DefSchLessFuncCtx( RecordType, FieldIndex, FieldType ) CDefSchOps<RecordType, FieldIndex, FieldType>::LessFuncCtx + + +//----------------------------------------------------------------------------- +// Specializations for string fields +//----------------------------------------------------------------------------- +template <class T, int I> +class CDefSchOps<T, I, char *> +{ +public: + static bool LessFunc( const T &lhs, const T &rhs ) + { + // Check that the field number is valid + COMPILE_TIME_ASSERT( I >= 0 && I < T::k_iFieldMax ); + + // Check to make sure this is indeed a string field + Field_t &fieldInfo = lhs.GetPSchema()->GetField( I ); + Assert( fieldInfo.BIsStringType() ); + if ( !fieldInfo.BIsStringType() ) + return false; + + // Read the data + uint32 cubRead; + const char *pchLhs = lhs.GetStringField( I, &cubRead ); + const char *pchRhs = rhs.GetStringField( I, &cubRead ); + + // Finally do the comparison + return CDefOps<const char *>::LessFunc( lhs, rhs ); + } + + static bool LessFuncCtx( const T &lhs, const T &rhs, void *pCtx ) + { + return LessFunc( lhs, rhs ); + } +}; + + +template <class T, int I> +class CDefSchOps<T, I, const char *> +{ +public: + static bool LessFunc( const T &lhs, const T &rhs ) + { + return CDefSchOps<T, I, char *>::LessFunc( lhs, rhs ); + } + + static bool LessFuncCtx( const T &lhs, const T &rhs, void *pCtx ) + { + return LessFunc( lhs, rhs ); + } +}; + + +//----------------------------------------------------------------------------- +// Purpose: Provide a convenient object to pass around to represent a type +// of record +//----------------------------------------------------------------------------- +class CRecordType +{ +public: + virtual int GetITable() const = 0; + virtual CRecordBase *Create() const = 0; + + CSchema *GetSchema() const; + CRecordInfo *GetRecordInfo() const; +protected: +private: +}; + +template <typename TRecord> +class CRecordTypeConcrete: public CRecordType +{ +public: + virtual int GetITable() const { return TRecord::k_iTable; } + virtual CRecordBase *Create() const { return new TRecord(); } +}; + +#define RTYPE( recordClass ) CRecordTypeConcrete<recordClass>() + +} // namespace GCSDK + +#include "tier0/memdbgoff.h" + +#endif // GCRECORD_H diff --git a/public/gcsdk/sqlaccess/recordinfo.h b/public/gcsdk/sqlaccess/recordinfo.h new file mode 100644 index 0000000..a4d332a --- /dev/null +++ b/public/gcsdk/sqlaccess/recordinfo.h @@ -0,0 +1,174 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//============================================================================= + +#ifndef GCRECORDINFO_H +#define GCRECORDINFO_H + +namespace GCSDK +{ + +typedef CUtlMap<const char *,int> CMapIColumnInfo; + +// -------------------------------------------------------------------------- +// Information about a column in a record (table or result set) +class CColumnInfo +{ +public: + CColumnInfo(); + ~CColumnInfo() { } + + void Set( const char *pchName, int nSQLColumn, EGCSQLType eGCSQLType, int cubFixedSize, int nColFlags, int cubMaxSize ); + const char *GetName() const { return m_rgchName; } + int GetSQLColumn() const { return m_nSQLColumn; } + EGCSQLType GetType() const { return m_eType; } + int GetFixedSize() const { return m_cubFixedSize; } + int GetMaxSize() const { return m_cchMaxSize; } + int GetChecksum() const { Assert( m_bHaveChecksum ); return m_nChecksum; } + bool BIsVariableLength() const; + int GetColFlags() const { return m_nColFlags; } + void GetColFlagDescription( char* pstrOut, int cubOutLength ) const; + int GetConstraintColFlags() { return m_nColFlags & k_nColFlagAllConstraints; } + void SetColFlagBits( int nColFlag ); + bool BIsIndexed() const { return 0 != ( m_nColFlags & k_nColFlagIndexed ); } + bool BIsClustered() const { return 0 != ( m_nColFlags & k_nColFlagClustered ); } + bool BIsUnique() const { return 0 != ( m_nColFlags & k_nColFlagUnique ); } + bool BIsAutoIncrement() const { return 0 != ( m_nColFlags & k_nColFlagAutoIncrement ); } + bool BIsPrimaryKey() const { return 0 != ( m_nColFlags & k_nColFlagPrimaryKey ); } + bool BIsExplicitlyIndexed() const { return BIsIndexed() && !( BIsPrimaryKey() || BIsUnique() ); } + bool BIsExplicitlyUnique() const { return BIsUnique() && !BIsPrimaryKey(); } + bool BIsInsertable() const { return !BIsAutoIncrement(); } + void CalculateChecksum(); + void ValidateColFlags() const; + bool operator==( const CColumnInfo& refOther ) const; + bool operator!=( const CColumnInfo& refOther ) const + { + return ! operator==( refOther ); + } + +#ifdef DBGFLAG_VALIDATE + void Validate( CValidator &validator, const char *pchName ); +#endif // DBGFLAG_VALIDATE +private: + CColumnInfo( CColumnInfo& ); // no copy constructor, disable default copy constructor + CColumnInfo& operator = ( CColumnInfo& ); // no assignment operator, disable default assignment operator + char m_rgchName[k_cSQLObjectNameMax+1]; + + EGCSQLType m_eType; // GC-based enum data type of this column + int m_nColFlags; // flags for this column + int m_nSQLColumn; // column # in SQL database to bind to, starts at 1. + int m_cubFixedSize; // if fixed size, the fixed size in bytes; else 0 + int m_cchMaxSize; // if variable size, the maximum size; else 0 + int m_nChecksum; // checksum of this column info for quick comparisons + bool m_bHaveChecksum; // have we calculated a checksum yet? +}; + +// -------------------------------------------------------------------------- +// Information about a record (table or result set) +class CRecordInfo : public CRefCount +{ +public: + CRecordInfo(); + + void InitFromDSSchema( CSchema *pSchema ); + + void SetName( const char *pchName ); + const char *GetName() const { return m_rgchName; } + void AddColumn( const char *pchName, int nSQLColumn, EGCSQLType eGCSQLType, int cubFixedSize, int nColFlags, int cubMaxSize ); + void SetAllColumnsAdded() { m_bAllColumnsAdded = true; } + void PrepareForUse(); + int GetFixedSize() const { return m_cubFixedSize; } + int GetNumColumns() const { return m_VecColumnInfo.Count(); } + const CColumnInfo &GetColumnInfo( uint32 unColumn ) const { return m_VecColumnInfo[unColumn]; } + CColumnInfo &GetColumnInfo( uint32 unColumn ) { return m_VecColumnInfo[unColumn]; } + bool BFindColumnByName( const char *pchName, int *piColumn ); + bool BPreparedForUse() const { return m_bPreparedForUse; } + void EnsureCapacity( int cColumns ) { m_VecColumnInfo.EnsureCapacity( cColumns ); } + int GetChecksum(); + ESchemaCatalog GetESchemaCatalog() const { return m_eSchemaCatalog; } + void SetESchemaCatalog( ESchemaCatalog e ) { m_eSchemaCatalog = e; } + bool EqualTo( CRecordInfo* pOther ); + bool CompareIndexLists( CRecordInfo *pOther ); + bool CompareFKs( CRecordInfo *pOther ); + bool CompareFTSIndexLists( CRecordInfo *pOther ) const; + EPrimaryKeyType GetPrimaryKeyType() const { return m_nHasPrimaryKey; } + bool BHasPrimaryKey() { return GetPrimaryKeyType() != k_EPrimaryKeyTypeNone; } + const FieldSet_t& GetPKFields() { Assert( BHasPrimaryKey()); return GetIndexFields( )[ m_iPKIndex ]; } + const CUtlVector<FieldSet_t>& GetIndexFields() const { return m_VecIndexes; } + int GetIndexFieldCount() const { return m_VecIndexes.Count(); } + int FindIndex( CRecordInfo *pRec, const FieldSet_t& fieldSet ); + int FindIndexByName( const char *pszName ) const; + int GetPKIndex() const { return m_iPKIndex; } + void SetPKIndex( int i ) { m_iPKIndex = i; } + int AddIndex( const FieldSet_t& fieldSet ); + void GetIndexFieldList( CFmtStr1024 *pstr, int nIndents ) const; + int GetTableID() const { return m_nTableID; } + void SetTableID( int nTableID ) { m_nTableID = nTableID; } + bool BHasIdentity() const; + + // full-text index + CUtlVector<int> & GetFTSFields() { return m_vecFTSFields; } + bool BHasFTSIndex() const { return m_vecFTSFields.Count() > 0; } + void AddFTSFields( CUtlVector< int > &refVecFields ); + int GetFullTextCatalogIndex() { return m_nFullTextCatalogIndex; } + + // foreign keys + void AddFK( const FKData_t &fkData ); + void GetFKListString( CFmtStr1024 *pstr, int nIndents ); + int GetFKCount(); + FKData_t &GetFKData( int iIndex ); + + + static CRecordInfo *Alloc(); +#ifdef DBGFLAG_VALIDATE + static void ValidateStatics( CValidator &validator, const char *pchName ); + void Validate( CValidator &validator, const char *pchName ); +#endif //DBGFLAG_VALIDATE + + + // note: destructor is private. This is a ref-counted object, private destructor ensures callers can't accidentally delete + // directly, or declare on stack + virtual ~CRecordInfo() { } + +private: + virtual void DestroyThis(); + void CalculateChecksum(); + void BuildColumnNameIndex(); + + char m_rgchName[k_cSQLObjectNameMax+1]; + int m_nTableID; // Object_ID if this table in SQL Server + CUtlVector<CColumnInfo> m_VecColumnInfo; // Vector of columns in this record + CMapIColumnInfo m_MapIColumnInfo; // Map of name->column index for quick lookup by name + EPrimaryKeyType m_nHasPrimaryKey; // Does this table contain a column that is a primary key? + int m_iPKIndex; // index info m_VecIndexes of our PK index; -1 if no PK + CUtlVector<FieldSet_t> m_VecIndexes; // vector of all fields in all indexes + int m_cubFixedSize; // Sum of data sizes for all fixed size columns + bool m_bAllColumnsAdded; // Have all columns been added + bool m_bPreparedForUse; // Have we finished being initialized? + bool m_bHaveColumnNameIndex; // Have we created a column name index? (Only generated if someone asks.) + bool m_bHaveChecksum; // Have we generated a checksum? (Only generated if someone asks.) + int m_nChecksum; // checksum of this record info for quick comparisons - includes all columns + ESchemaCatalog m_eSchemaCatalog; // what catalog owns this object? + CUtlVector< int > m_vecFTSFields; // which fields have FTS indexing? + int m_nFullTextCatalogIndex; // index of catalog for FTS index, if we get one + CUtlVector<FKData_t> m_VecFKData; // vector of all FK relationships defined on this table + + CThreadMutex m_Mutex; + static CThreadSafeClassMemoryPool<CRecordInfo> sm_MemPoolRecordInfo; + +#ifdef _DEBUG + // validation tracking + static CUtlRBTree<CRecordInfo *, int > sm_mapPMemPoolRecordInfo; + static CThreadMutex sm_mutexMemPoolRecordInfo; +#endif +}; + + +int __cdecl CompareColumnInfo( const CColumnInfo *pColumnInfoLeft, const CColumnInfo *pColumnInfoRight ); + + +} // namespace GCSDK +#endif // GCRECORDINFO_H diff --git a/public/gcsdk/sqlaccess/schema.h b/public/gcsdk/sqlaccess/schema.h new file mode 100644 index 0000000..b6ea819 --- /dev/null +++ b/public/gcsdk/sqlaccess/schema.h @@ -0,0 +1,601 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//============================================================================= + +#ifndef GCSCHEMA_H +#define GCSCHEMA_H +#ifdef _WIN32 +#pragma once +#endif + +namespace GCSDK +{ +const int k_nColFlagIndexed = 0x0001; // this column is indexed. +const int k_nColFlagUnique = 0x0002; // this column has a uniqueness constraint - creates implicit index +const int k_nColFlagPrimaryKey = 0x0004; // this column has a primary key constraint - creates implicit uniqueness constraint & implicit index +const int k_nColFlagAutoIncrement = 0x0008; // this column can have it's values created implicitly by the sql counter +const int k_nColFlagClustered = 0x0010; // this column is clustered + +const int k_nColFlagAllConstraints = k_nColFlagUnique | k_nColFlagPrimaryKey; + +class CRecordInfo; +struct VarFieldBlockInfo_t; +struct VarFieldBlockInfo_t; +struct Field_t; +struct VarField_t; + +// Function type for altering field types when converting schemas +typedef void (* PfnAlterField_t )( void *pvDest, const void *pvSrc ); + +// EPrimaryKeyType +// This shows if a table has a primary key and, if so, if it has multiple columns +// or not. +enum EPrimaryKeyType +{ + k_EPrimaryKeyTypeNone = 0, // none at all + k_EPrimaryKeyTypeSingle, // single-column primary key + k_EPrimaryKeyTypeMulti, // multi-column primary key +}; + + +// EWipePolicy +// This tells us if a table is supposed to be wiped before all tests, +// before all tests except stress tests, or not to be wiped before tests. +enum EWipePolicy +{ + k_EWipePolicyPreserveAlways = 0, // don't wipe table + k_EWipePolicyPreserveForStress = 1, // preserve for stress tests, wipe before regular tests + k_EWipePolicyWipeForAllTests = 2, // wipe table prior to all tests +}; + +//----------------------------------------------------------------------------- +// Field +// This defines the schema of a single field in one of our tables. +//----------------------------------------------------------------------------- +struct Field_t +{ + bool BGetIntData( uint8 *pubRecord, uint64 *pulRet ) const; + bool BGetFloatData( uint8 *pubRecord, float *fRet ) const; + bool SetIntData( uint8 *pubRecord, uint64 ulValue ) const; + // Not all fields are updated wholly at a time + int CubFieldUpdateSize() const; + // Handy helpers - complex fields like "rolling unique" are + // still binary + bool BIsStringType() const; + + bool BIsVariableLength() const; + + // Members that get serialized + EGCSQLType m_EType; // Field type + uint32 m_cubLength; // Length of the field in bytes + uint32 m_cchMaxLength; // maximum length of the field in characters + char m_rgchName[k_cSQLObjectNameMax]; // Human-readable name of this field + char m_rgchSQLName[k_cSQLObjectNameMax]; // SQL name of this field + + // Members that don't get serialized + uint32 m_nColFlags; // k_nColFlag* bits for this field + uint32 m_dubOffset; // Offset of this field from beginning of record +}; + + +// -------------------------------------------------------------------------- +// Information about a foreign key relationship defined on a table +struct FKColumnRelation_t +{ + char m_rgchCol[k_cSQLObjectNameMax+1]; + char m_rgchParentCol[k_cSQLObjectNameMax+1]; + + FKColumnRelation_t() + { + Q_memset( m_rgchCol, 0, Q_ARRAYSIZE( m_rgchCol ) ); + Q_memset( m_rgchParentCol, 0, Q_ARRAYSIZE( m_rgchParentCol ) ); + } + + bool operator==( const FKColumnRelation_t &other ) const + { + if ( Q_stricmp( m_rgchCol, other.m_rgchCol ) ) + return false; + if ( Q_stricmp( m_rgchParentCol, other.m_rgchParentCol ) ) + return false; + return true; + } +}; + +struct FKData_t +{ + char m_rgchName[k_cSQLObjectNameMax+1]; + char m_rgchParentTableName[k_cSQLObjectNameMax+1]; + CCopyableUtlVector<FKColumnRelation_t> m_VecColumnRelations; + EForeignKeyAction m_eOnDeleteAction; + EForeignKeyAction m_eOnUpdateAction; + + FKData_t() + { + Q_memset( m_rgchName, 0, Q_ARRAYSIZE( m_rgchName ) ); + Q_memset( m_rgchParentTableName, 0, Q_ARRAYSIZE( m_rgchParentTableName ) ); + m_eOnDeleteAction = k_EForeignKeyActionNoAction; + m_eOnUpdateAction = k_EForeignKeyActionNoAction; + } + + bool operator==( const FKData_t &other ) const + { + if ( Q_stricmp( m_rgchName, other.m_rgchName ) ) + return false; + + if ( Q_stricmp( m_rgchParentTableName, other.m_rgchParentTableName ) ) + return false; + + if ( m_eOnDeleteAction != other.m_eOnDeleteAction || m_eOnUpdateAction != other.m_eOnUpdateAction ) + return false; + + FOR_EACH_VEC( m_VecColumnRelations, i ) + { + bool bFoundInOther = false; + const FKColumnRelation_t &cols = m_VecColumnRelations[i]; + + FOR_EACH_VEC( other.m_VecColumnRelations, j ) + { + const FKColumnRelation_t &colsOther = other.m_VecColumnRelations[j]; + if ( cols == colsOther ) + { + bFoundInOther = true; + break; + } + } + + if ( !bFoundInOther ) + return false; + } + return true; + } + +#ifdef DBGFLAG_VALIDATE + // Validate our internal structures + void Validate( CValidator &validator, const char *pchName ) + { + VALIDATE_SCOPE(); + ValidateObj( m_VecColumnRelations ); + } +#endif + +}; + + +#pragma pack( push, 1 ) + + +//----------------------------------------------------------------------------- +// SerSchema +// Defines the binary serialization format for a schema. +//----------------------------------------------------------------------------- +struct SerSchema_t +{ + uint32 m_cub; // Size of the whole schema (including header and fields) + int32 m_iTable; // Our table's iTable + char m_rgchName[k_cSQLObjectNameMax]; // Human-readable name of this table + int16 m_cField; // # of fields in the schema (int16 for backward-compatibility reasons) + int16 m_ETableGroup; // Our table's TableGroup (int16 for backward-compatibility reasons) - OBSOLETE +}; + + +//----------------------------------------------------------------------------- +// SerField +// Defines the binary serialization format for a field in a schema +// Note that certain fields are missing from this because we only use serialized +// schemas for schema mapping. Fields that only affect runtime behavior (like +// indexing) are always defined by the intrinsic schema. +//----------------------------------------------------------------------------- +struct SerField_t +{ + int32 m_EType; // Field type + uint32 m_cubLength; // Length of field data in bytes + // For rolling fields, high 16 bits are the + // size of each element + + char m_rgchName[k_cSQLObjectNameMax];// Human-readable name of this field + char m_rgchSQLName[k_cSQLObjectNameMax]; // SQL name of this field +}; + +#pragma pack( pop ) + + +//----------------------------------------------------------------------------- +// Schema conversion instructions +// These specify various operations that can be performed when converting +// from one Schema to another. +//----------------------------------------------------------------------------- +struct DeleteField_t +{ + char m_rgchFieldName[k_cSQLObjectNameMax]; // Name of the field to delete +}; + +struct RenameField_t +{ + char m_rgchFieldNameOld[k_cSQLObjectNameMax]; // Rename a field with this name + int m_iFieldDst; // to this field +}; + +struct AlterField_t +{ + char m_rgchFieldNameOld[k_cSQLObjectNameMax]; // Name of field in the old schema + int m_iFieldDst; // iField of it in the new + PfnAlterField_t m_pfnAlterFunc; // Function to translate the data +}; + +//----------------------------------------------------------------------------- +// FieldSet_t describes a collection of fields in an index, as well as +// attributes of the index itself +//----------------------------------------------------------------------------- + +class FieldSet_t +{ +public: + FieldSet_t( bool bUnique, bool bClustered, const CUtlVector<int>& vecFields, const char* pstrIndexName ) + : m_bClustered( bClustered ), m_bUnique( bUnique ) + { + m_VecFields = vecFields; + + // zero means to use the server default + m_nFillFactor = 0; + + // null name is allowed for primary keys + if ( pstrIndexName != NULL ) + Q_strncpy( m_szIndexName, pstrIndexName, Q_ARRAYSIZE( m_szIndexName ) ); + else + m_szIndexName[0] = 0; + } + + FieldSet_t( ) + { + } + + ~FieldSet_t( ) + { + } + + FieldSet_t( const FieldSet_t &refOther ) + { + DoAssignment( refOther ); + } + + FieldSet_t& operator=( const FieldSet_t &refOther ) + { + DoAssignment( refOther ); + return *this; + } + + void DoAssignment( const FieldSet_t &refOther ) + { + m_VecFields = refOther.m_VecFields; + m_VecIncluded = refOther.m_VecIncluded; + m_bClustered = refOther.m_bClustered; + m_bUnique = refOther.m_bUnique; + m_nFillFactor = refOther.m_nFillFactor; + Q_strncpy( m_szIndexName, refOther.m_szIndexName, Q_ARRAYSIZE( m_szIndexName ) ); + + } + + // get count of fields in this index + int GetCount() const + { + return m_VecFields.Count(); + } + + // get count of included fields in this index + int GetIncludedCount() const + { + return m_VecIncluded.Count(); + } + + void AddIncludedColumn( int nIndex ) + { + m_VecIncluded.AddToTail( nIndex ); + } + + void AddIncludedColumns( const CUtlVector<int> &refVec ) + { + m_VecIncluded.AddVectorToTail( refVec ); + } + + // get a particular field ID + // the returned index is into the VecFields of the associated schema + int GetField( int nIndex ) const + { + return m_VecFields[ nIndex ]; + } + + int GetIncludedField( int nIndex ) const + { + return m_VecIncluded[ nIndex ]; + } + + // is this index clustered? + bool IsClustered() const + { + return m_bClustered; + } + + // is this index unique? + bool IsUnique() const + { + return m_bUnique; + } + + void SetClustered( bool bIsClustered ) + { + m_bClustered = bIsClustered; + } + + void SetFillFactor( int nFactor ) + { + Assert( nFactor >= 0 && nFactor <= 100 ); + m_nFillFactor = nFactor; + } + + int GetFillFactor( ) const + { + return m_nFillFactor; + } + + const char* GetIndexName() const + { + return m_szIndexName; + } + + // determine if this fieldset is equal to the other one + static bool CompareFieldSets( const FieldSet_t& refThis, CRecordInfo* pRecordInfoThis, + const FieldSet_t& refOther, CRecordInfo* pRecordInfoOther ); + +#ifdef DBGFLAG_VALIDATE + // Validate our internal structures + void Validate( CValidator &validator, const char *pchName ) + { + VALIDATE_SCOPE(); + m_VecFields.Validate( validator, "m_VecFields" ); + m_VecIncluded.Validate( validator, "m_VecIncluded" ); + } +#endif + +private: + CUtlVector<int> m_VecFields; // ids of fields; indexes into m_VecFields of CSchema for a table + CUtlVector<int> m_VecIncluded; // ids of included fields + int m_nFillFactor; // fill factor for the index; zero means to use the server's default + char m_szIndexName[k_cSQLObjectNameMax]; // name of this index + bool m_bClustered:1; // is this index clustered? + bool m_bUnique:1; // is this index unique? +}; + +//----------------------------------------------------------------------------- + +enum ESchemaCatalog +{ + k_ESchemaCatalogInvalid = -1, + k_ESchemaCatalogMain = 0, // main GC catalog + k_ESchemaCatalogOGS = 4, // operational game stats +}; + +extern const char* PchNameFromESchemaCatalog( ESchemaCatalog e ); + + +//----------------------------------------------------------------------------- +// CSchema +// This defines the schema for a single table. The schema essentially defines +// what's in the table (ie, field 0 is a 32 char string called "Name", etc.) +// The schema is in charge of manipulating individual records within the table. +//----------------------------------------------------------------------------- + +class CSchemaFull; + +class CSchema +{ +public: + + // Constructors & destructors + CSchema(); + ~CSchema(); + + // Recalculates field offsets and maximum record size. + // Must be called after changing schema. + void CalcOffsets(); + + // called to make final calculations when all fields/indexes/etc have been added and the schema is ready to be used + void PrepareForUse(); + + // Sizing information + uint32 CubSerialSchema() const { return( sizeof( SerSchema_t ) + m_VecField.Count() * sizeof( SerField_t ) ); } + + // Size of total fixed-length portion of record + uint32 CubRecordFixed() const { return( m_cubRecord ); } + + // Size of the total variable-length portion of record (zero if no var-length fields) + uint32 CubRecordVariable( const void *pvRecord ) const; + + // Does this record have variable-length fields? + bool BHasVariableFields() const { return m_bHasVarFields; } + + VarFieldBlockInfo_t* PVarFieldBlockInfoFromRecord( const void *pvRecord ) const; + + // Access field data - fixed or variable (may return NULL/0 for empty var field) + bool BGetFieldData( const void *pvRecord, int iField, uint8 **ppubField, uint32 *pcubField ) const; + bool BSetFieldData( void *pvRecord, int iField, uint8 *pubField, uint32 cubField, bool *pbVarBlockRealloced ); + + bool BGetVarField( const void *pvRecord, const VarField_t *pVarField, uint8 **ppubField, uint32 *pcubField ) const; + bool BSetVarField( void *pvRecord, VarField_t *pVarField, const void *pvData, uint32 cubData, bool *pbRealloced, bool bFreeOnRealloc ); + + // Adjust var-block pointer, if present, to point just after the fixed part of the record + void FixupDeserializedRecord( void *pvRecord ); + + // Render a record in text format + void RenderRecord( uint8 *pubRecord ); + void RenderField( uint8 *pubRecord, int iField, int cchBuffer, char *pchBuffer ); + + // Accessors + void SetITable( int iTable ) { m_iTable = iTable; } + int GetITable() const { return m_iTable; } + int GetCField() const { return m_VecField.Count(); } + void SetReportingInterval( int nInterval ) { m_nReportingInterval = nInterval; } + int GetReportingInterval( ) const { return m_nReportingInterval; } + Field_t &GetField( int iField ) { return m_VecField[iField]; } + const Field_t &GetField( int iField ) const { return m_VecField[iField]; } + VarField_t *GetPVarField( void *pvRecord, int iField ) { return ( VarField_t * )( ( uint8 * ) pvRecord + m_VecField[iField].m_dubOffset ); } + void SetName( const char *pchName ) { Q_strncpy( m_rgchName, pchName, sizeof( m_rgchName ) ); } + const char *GetPchName() const { return m_rgchName; } + const FieldSet_t& GetPKFields() { Assert( m_iPKIndex != -1 ); return GetIndexes()[m_iPKIndex]; } + int GetPKIndex() const { return m_iPKIndex; } + const CUtlVector<FieldSet_t>& GetIndexes() { return m_VecIndexes; } + const CUtlVector<int>& GetFTSColumns() { return m_VecFullTextIndexes; } + int GetFTSIndexCatalog() const { return m_nFullTextIndexCatalog; } + + ESchemaCatalog GetESchemaCatalog() const { return m_eSchemaCatalog; } + void SetESchemaCatalog( ESchemaCatalog eSchemaCatalog ) { m_eSchemaCatalog = eSchemaCatalog; } + + // If cRecordMax is non-zero, this is a rolling table that only + // holds on to cRecordMax records at most. + void SetCRecordMax( int cRecordMax ) { m_cRecordMax = cRecordMax; } + int GetCRecordMax() const { return m_cRecordMax; } + + // Is this table for TESTs only? + void SetBTestTable( bool bTestTable ) { m_bTestTable = bTestTable; } + bool GetBTestTable() const { return m_bTestTable; } + + // Randomly init a record or field to random values + void InitRecordRandom( uint8 *pubRecord, uint32 unPrimaryIndex, bool *pbVarBlockRealloced, bool bFreeVarBlockOnRealloc ); + void SetFieldRandom( uint8 *pubRecord, int iField, bool *pbVarBlockRealloced, bool bFreeVarBlockOnRealloc ); + + // Checksum the schema + uint32 CalcChecksum(); + + // pre-allocate space in the field array + void EnsureFieldCount( int cFields ) + { + m_VecField.EnsureCapacity( cFields ); + } + + // This adds a field from our intrinsic schema to us + void AddField( char *pchName, char *pchSQLName, EGCSQLType eType, uint32 cubSize, int cchMaxLength ); + void AddIntField( char *pchName, char *pchSQLName, EGCSQLType eType, int cubSize ); + + // We want to make a particular field the primary key + int PrimaryKey( bool bClustered, int nFillFactor, const char *pchName ); + + // we want to make a particular list of fields the primary key + int PrimaryKeys( bool bClustered, int nFillFactor, const char *pchNames ); + + // We want to index a particular field by name + int IndexField( const char *pchName, const char *pchIndexName ); + + // We want to index a particular list of fields in a group + int IndexFields( const char *pchIndexName, const char *pchNames ); + + // We want a certain index to additionally include a list of fields + void AddIncludedFields( const char *pchIndexName, const char *pchNames ); + + // We want to unique index a particular list of fields in a group + int UniqueFields( const char *pchIndexName, const char *pchNames ); + + // add a full-text index to the given column + void AddFullTextIndex( CSchemaFull *pSchemaFull, const char *pchCatalogName, const char *pchColumnName ); + + // We want to index a particular field by field number + // (field number is an index into the m_VecField array) + int AddIndexToFieldNumber( int iField, const char *pchIndexName, bool bClustered ); + + // We want to index a particular set of fields + // pchNames includes the names, separated by commas, of each field + int AddIndexToFieldList( const char *pchNames, const char *pchIndexName, int nFlags, int nFillFactor ); + + // We want a unique index on a particular field + int UniqueField( const char *pchName, const char *pchIndexName ); + + // We want to have a clustered index on a particular field by name + int ClusteredIndexField( int nFillFactor, const char *pchName, const char *pchIndexName ); + + // We want to index a particular list of fields in a group + int ClusteredIndexFields( int nFillFactor, const char *pchIndexName, const char *pchNames ); + + // We want an autoinc on a particular field + void AutoIncrementField( char *pchName ); + + // catalog on which we'll enable FTS + void EnableFTS( ESchemaCatalog eCatalog ); + + // adds a full text catalog with the given name on the identified fileset + void AddFullTextCatalog( ESchemaCatalog eCatalog, const char *pstrCatalogName, const char *pstrFileGroupName ); + + // Adds a FK on the table + void AddFK( const char* pchName, const char* pchColumn, const char* pchParentTable, const char* pchParentColumn, EForeignKeyAction eOnDeleteAction, EForeignKeyAction eOnUpdateAction ); + + // Access FK data + int GetFKCount(); + FKData_t &GetFKData( int iIndex ); + + void SetTestWipePolicy( EWipePolicy policy ) { m_wipePolicy = policy; } + EWipePolicy GetTestWipePolicy() const { return m_wipePolicy; } + + void SetBAllowWipeTableInProd( bool bVal ) { m_bAllowWipeInProd = bVal; } + bool BAllowWipeTableInProd() const { return m_bAllowWipeInProd; } + + void SetPrepopulatedTable( ) { m_bPrepopulatedTable = true; } + bool BPrepopulatedTable( ) const { return m_bPrepopulatedTable; } + + // Find the field with a given name (returns k_iFieldNil if not found) + int FindIField( const char *pchName ); + int FindIFieldSQL( const char *pchName ); + + // Helper functions for recording schema conversion operations + void AddDeleteField( const char *pchFieldName ); + void AddRenameField( const char *pchFieldNameOld, const char *pchFieldNameNew ); + void AddAlterField( const char *pchFieldNameOld, const char *pchFieldNameNew, PfnAlterField_t pfnAlterField ); + + // Schema conversion helper: figure out what field to map a field from a different schema to + bool BCanConvertField( const char *pchFieldSrc, int *piFieldDst, PfnAlterField_t *ppfnAlterField ); + + CRecordInfo *GetRecordInfo() { return m_pRecordInfo; } + const CRecordInfo *GetRecordInfo() const { return m_pRecordInfo; } + + void Validate( CValidator &validator, const char *pchName ); // Validate our internal structures + void ValidateRecord( uint8 *pubRecord ); // Validate a record that uses our schema + + // cached queries + const char *GetInsertStatementText() const; + const char *GetMergeStatementTextOnPKWhenMatchedUpdateWhenNotMatchedInsert(); + const char *GetMergeStatementTextOnPKWhenNotMatchedInsert(); + +private: + int m_iTable; // The index of our table + int m_iPKIndex; // index into of m_VecIndexes of our PK index; k_iFieldNil if no PK + char m_rgchName[k_cSQLObjectNameMax]; // Name of this table + CUtlVector<Field_t> m_VecField; // All the fields that make up the schema + CUtlVector<FieldSet_t> m_VecIndexes; // vector of all fields in all indexes + int m_cRecordMax; // Max # records in the table (for rolling tables) + bool m_bTestTable; // Table exists only for tests + bool m_bAllowWipeInProd; // should we allow WipeTable operations on this table in the beta/public universe? + EWipePolicy m_wipePolicy; // should this table be wiped between all tests, no tests, or non-stress tests? + bool m_bHasVarFields; // True if this table has variable-length fields + bool m_bPrepopulatedTable; // true if this table is pre-populated + EPrimaryKeyType m_nHasPrimaryKey; // what kind of PK do we have, if any? + CRecordInfo *m_pRecordInfo; // The record description corresponding to this schema. (Similar info, record description is new form, have both for a while during DS->SQL switch) + CUtlVector<int> m_VecFullTextIndexes; // vector of indexes into m_VecField of fields covered by this table's full-text index. + int m_nFullTextIndexCatalog; // index of catalog to use for creating full-text indexes + CUtlVector<FKData_t> m_VecFKData; // data on foreign keys for this schema object + + uint32 m_cubRecord; // Binary record length + int m_nReportingInterval; // reporting interval of this table if stats; 0 if not stats + ESchemaCatalog m_eSchemaCatalog; // what catalog does this table live in? + + // Schema conversion instructions + CUtlVector<DeleteField_t> m_VecDeleteField; + CUtlVector<RenameField_t> m_VecRenameField; + CUtlVector<AlterField_t> m_VecAlterField; + + // Cached queries + mutable CUtlString m_sInsertStatementText; // Cached insert statement for the table + CUtlString m_sMergeStatementTextOnPKWhenMatchedUpdateWhenNotMatchedInsert; // Cached insert or update via MERGE statement for the table + CUtlString m_sMergeStatementTextOnPKWhenNotMatchedInsert; // Cached insert via MERGE statement for the table +}; + +} // namespace GCSDK +#endif // GCSCHEMA_H diff --git a/public/gcsdk/sqlaccess/schemafull.h b/public/gcsdk/sqlaccess/schemafull.h new file mode 100644 index 0000000..c971478 --- /dev/null +++ b/public/gcsdk/sqlaccess/schemafull.h @@ -0,0 +1,284 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//============================================================================= + +#ifndef GCSCHEMAFULL_H +#define GCSCHEMAFULL_H +#ifdef _WIN32 +#pragma once +#endif + + +namespace GCSDK +{ + + +//----------------------------------------------------------------------------- +// SerSchemaFull +// This defines the binary serialization format for a CSchemaFull +//----------------------------------------------------------------------------- +struct SerSchemaFull_t +{ + enum EVersion + { + k_ECurrentVersion = 1, + }; + + int32 m_nVersion; // version of serialization format + int32 m_cSchema; // # of schema we contain +}; + + +//----------------------------------------------------------------------------- +// CFTSCatalogInfo +// information about a full text search catalog object in our schema +//----------------------------------------------------------------------------- +class CFTSCatalogInfo +{ +public: + enum ESchemaCatalog m_eCatalog; + const char *m_pstrName; + int m_nFileGroup; + + CFTSCatalogInfo() + : m_pstrName( NULL ), + m_eCatalog( k_ESchemaCatalogInvalid ) + { + } + + ~CFTSCatalogInfo() + { + free( (void*) m_pstrName); + } + + CFTSCatalogInfo( const CFTSCatalogInfo &refOther ) + { + m_eCatalog = refOther.m_eCatalog; + m_nFileGroup = refOther.m_nFileGroup; + if ( refOther.m_pstrName != NULL ) + m_pstrName = strdup( refOther.m_pstrName ); + else + m_pstrName = NULL; + } + +#ifdef DBGFLAG_VALIDATE + void Validate( CValidator &validator, const char *pchName ) // Validate our internal structures + { + validator.ClaimMemory( (void *) m_pstrName ); + } +#endif +}; + + +//----------------------------------------------------------------------------- +// SchemaFull conversion instructions +// These specify various operations that can be performed when converting +// from one SchemaFull to another. +//----------------------------------------------------------------------------- +struct DeleteTable_t +{ + char m_rgchTableName[k_cSQLObjectNameMax]; // Name of the table to delete +}; + +struct RenameTable_t +{ + char m_rgchTableNameOld[k_cSQLObjectNameMax]; // Rename a table with this name + int m_iTableDst; // to this table +}; + + +enum ETriggerType +{ + k_ETriggerType_Invalid, + k_ETriggerType_After_Insert, + k_ETriggerType_InsteadOf_Insert, + k_ETriggerType_After_Delete, + k_ETriggerType_InsteadOf_Delete, + k_ETriggerType_After_Update, + k_ETriggerType_InsteadOf_Update, +}; + +class CTriggerInfo +{ +public: + CTriggerInfo() + : m_eTriggerType( k_ETriggerType_Invalid ), + m_bMatched( false ) + { + } + + // are these equal for identity? + bool operator==( const CTriggerInfo& refOther ) const + { + if ( 0 != Q_stricmp( m_szTriggerTableName, refOther.m_szTriggerTableName ) ) + return false; + if ( 0 != Q_stricmp( m_szTriggerName, refOther.m_szTriggerName ) ) + return false; + + // they're equal! + return true; + } + + // if the identity is the same, this will tell if text or type differs + bool IsDifferent( const CTriggerInfo& refOther ) const + { + if ( m_eTriggerType != refOther.m_eTriggerType ) + return false; + if ( m_strText != refOther.m_strText ) + return false; + + // they're equal! + return true; + } + + const char* GetTriggerTypeString() const + { + const char *pstrSQL = "~~ unknown trigger type syntax error ~~"; + + switch ( m_eTriggerType ) + { + case k_ETriggerType_After_Insert: + pstrSQL = "AFTER INSERT"; + break; + case k_ETriggerType_InsteadOf_Insert: + pstrSQL = "INSTEAD OF INSERT"; + break; + case k_ETriggerType_After_Delete: + pstrSQL = "AFTER DELETE"; + break; + case k_ETriggerType_InsteadOf_Delete: + pstrSQL = "INSTEAD OF DELETE"; + break; + case k_ETriggerType_After_Update: + pstrSQL = "AFTER UPDATE"; + break; + case k_ETriggerType_InsteadOf_Update: + pstrSQL = "INSTEAD OF UPDATE"; + break; + + default: + case k_ETriggerType_Invalid: + /* initialize is fine, thanks */ + break; + } + + return pstrSQL; + } + + bool m_bMatched; // got matched during schema convert + ETriggerType m_eTriggerType; // what kinda trigger is this? + ESchemaCatalog m_eSchemaCatalog; // catalog where this trigger lives + char m_szTriggerName[k_cSQLObjectNameMax]; // name of the trigger object + char m_szTriggerTableName[k_cSQLObjectNameMax]; // name of the table hosting this trigger + CUtlString m_strText; // text of the trigger + + // Validate our internal structures +#ifdef DBGFLAG_VALIDATE + void Validate( CValidator &validator, const char *pchName ) + { + m_strText.Validate( validator, pchName ); + } +#endif +}; + + +//----------------------------------------------------------------------------- +// CSchemaFull +// This defines the schema for the entire data store. It's essentially just +// a collection of CSchema, which define the schema for individual tables. +//----------------------------------------------------------------------------- +class CSchemaFull +{ +public: + // Constructors & destructors + CSchemaFull(); + ~CSchemaFull(); + + void Uninit(); + + // add a new schema and return its pointer. + CSchema *AddNewSchema( int iTable, ESchemaCatalog eCatalog, const char *pstrName ) + { + CSchema &refNewSchema = m_VecSchema[m_VecSchema.AddToTail()]; + refNewSchema.SetName( pstrName ); + refNewSchema.SetESchemaCatalog( eCatalog ); + SetITable( &refNewSchema, iTable ); + return &refNewSchema; + } + + + // Accessors + int GetCSchema() const { return m_VecSchema.Count(); } + CSchema &GetSchema( int iSchema ) { return m_VecSchema[iSchema]; } + uint32 GetCheckSum() const { return m_unCheckSum; } + const char *GetDefaultSchemaNameForCatalog( ESchemaCatalog eCatalog ); + + uint8 *GetPubScratchBuffer( ); + uint32 GetCubScratchBuffer() const { return m_cubScratchBuffer; } + + // Makes sure that a generated intrinsic schema is consistent + void CheckSchema( CSchema *pSchema, int cField, uint32 cubRecord ); + + // Find the table with a given name (returns -1 if not found) + int FindITable( const char *pchName ); + const char *PchTableFromITable( int iTable ); + + // Helper functions for recording schema conversion operations + void AddDeleteTable( const char *pchTableName ); + void AddRenameTable( const char *pchTableNameOld, const char *pchTableNameNew ); + void AddDeleteField( const char *pchTableName, const char *pchFieldName ); + void AddRenameField( const char *pchTableName, const char *pchFieldNameOld, const char *pchFieldNameNew ); + void AddAlterField( const char *pchTableName, const char *pchFieldNameOld, const char *pchFieldNameNew, PfnAlterField_t pfnAlterField ); + + // declare that a trigger is on a table + void AddTrigger( ESchemaCatalog eCatalog, const char *pchTableName, const char *pchTriggerName, ETriggerType eTriggerType, const char *pchTriggerText ); + + // Schema conversion helper: figure out what table to map a table from a different schema to + bool BCanConvertTable( const char *pchTableSrc, int *piTableDst ); + + // full text catalogs + void AddFullTextCatalog( enum ESchemaCatalog eCatalog, const char *pstrCatalogName, int nFileGroup ); + int GetFTSCatalogByName( enum ESchemaCatalog eCatalog, const char *pstrCatalogName ); + void EnableFTS( enum ESchemaCatalog eCatalog ); + int GetCFTSCatalogs() const { return m_vecFTSCatalogs.Count(); } + const CFTSCatalogInfo & GetFTSCatalogInfo( int nIndex ) const { return m_vecFTSCatalogs[nIndex]; } + + const CUtlVector< CTriggerInfo> & GetTriggerInfos( ) const { return m_VecTriggers; } + + // is the given schema catalog FTS enabled? + bool GetFTSEnabled( enum ESchemaCatalog eCatalog ); + + void Validate( CValidator &validator, const char *pchName ); // Validate our internal structures + + // sets tableID on CSchema, checking that it is not a duplicate + void SetITable( CSchema* pSchema, int iTable ); + void FinishInit(); // Recalculates some internal fields +private: + CUtlConstString m_strDefaultSchemaName; + + CUtlVector< CSchema > m_VecSchema; // Schema for tables in all catalogs + CUtlVector< CTriggerInfo > m_VecTriggers; // list of triggers in all catalogs + + // which schema catalogs have FTS enabled? + CUtlMap< ESchemaCatalog, bool > m_mapFTSEnabled; + + // list of catalogs; each is marked with the schema where it lives. + CUtlVector< CFTSCatalogInfo > m_vecFTSCatalogs; + + uint32 m_unCheckSum; // A simple checksum of our contents + + // SchemaFull conversion instructions + CUtlVector<DeleteTable_t> m_VecDeleteTable; + CUtlVector<RenameTable_t> m_VecRenameTable; + + uint8 *m_pubScratchBuffer; // Big enough to hold any record or sparse record in this schemafull + uint32 m_cubScratchBuffer; // Size of the scratch buffer +}; + +extern CSchemaFull & GSchemaFull(); + +} // namespace GCSDK +#endif // GCSCHEMAFULL_H diff --git a/public/gcsdk/sqlaccess/schemaupdate.h b/public/gcsdk/sqlaccess/schemaupdate.h new file mode 100644 index 0000000..17cff47 --- /dev/null +++ b/public/gcsdk/sqlaccess/schemaupdate.h @@ -0,0 +1,95 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Contains the job that's responsible for updating the database schema +// +//============================================================================= +#ifndef UPDATESCHEMA_H +#define UPDATESCHEMA_H +#ifdef _WIN32 +#pragma once +#endif + +namespace GCSDK +{ +typedef CUtlMap<const char *,CRecordInfo *> CMapPRecordInfo; + +enum EConversionMode +{ + k_EConversionModeInspectOnly, + k_EConversionModeConvertSafe, + k_EConversionModeConvertIrreversible +}; + +class CSchemaUpdate : public CRefCount +{ +public: + CSchemaUpdate(); + + void AddRecordInfoDesired( CRecordInfo *pRecordInfo ); + void AddFTSInfo( const CFTSCatalogInfo &refFTSInfo ); + void AddTriggerInfos( const CUtlVector< CTriggerInfo > &refTriggerInfo ); + + // input parameters + CMapPRecordInfo m_mapPRecordInfoDesired; + EConversionMode m_eConversionMode; + CUtlLinkedList< CFTSCatalogInfo > m_listFTSCatalogInfo; + CUtlVector< CTriggerInfo > m_vecTriggerInfo; + + // output parameters + bool m_bConversionNeeded; + bool m_bSkippedAChange; + int m_cTablesDesiredMissing; + int m_cTablesActualDifferent; + int m_cTablesActualUnknown; + int m_cTablesNeedingChange; + int m_cColumnsDesiredMissing; + int m_cColumnsActualDifferent; + int m_cColumnsActualUnknown; + + CFmtStr1024 m_sDetail; + +private: + virtual ~CSchemaUpdate(); +}; + +// -------------------------------------------------------------------------- + +class CJobUpdateSchema : public CGCJob +{ +public: + CJobUpdateSchema( CGCBase *pGC, int iTableCount ) : CGCJob( pGC ), m_mapSQLTypeToEType( DefLessFunc(int) ), m_iTableCount( iTableCount ) { } + bool BYieldingRunJob( void * ); +private: + bool BYieldingUpdateSchema( ESchemaCatalog eSchemaCatalog ); + SQLRETURN YieldingEnsureDatabaseSchemaCorrect( ESchemaCatalog eSchemaCatalog, CSchemaUpdate *pSchemaUpdate ); + EGCSQLType GetEGCSQLTypeForMSSQLType( int nType ); + bool YieldingBuildTypeMap( ESchemaCatalog eSchemaCatalog ); + SQLRETURN YieldingGetSchemaID( ESchemaCatalog eSchemaCatalog, int *pSchemaID ); + SQLRETURN YieldingGetRecordInfoForAllTables( ESchemaCatalog eSchemaCatalog, int nSchemaID, CMapPRecordInfo &mapPRecordInfo ); + SQLRETURN YieldingGetColumnInfoForTable( ESchemaCatalog eSchemaCatalog, CMapPRecordInfo &mapPRecordInfo, int nTableID, const char *pchTableName ); + SQLRETURN YieldingGetTableFKConstraints( ESchemaCatalog eSchemaCatalog, CRecordInfo *pRecordInfo ); + SQLRETURN YieldingGetColumnIndexes( ESchemaCatalog eSchemaCatalog, CRecordInfo *pRecordInfo ); + SQLRETURN YieldingGetTriggers( ESchemaCatalog eSchemaCatalog, int nSchemaID, CUtlVector< CTriggerInfo > &vecTriggerInfo ); + SQLRETURN YieldingCreateTable( ESchemaCatalog eSchemaCatalog, CRecordInfo *pRecordInfo ); + SQLRETURN YieldingAddIndex( ESchemaCatalog eSchemaCatalog, CRecordInfo *pRecordInfo, const FieldSet_t &refFields ); + SQLRETURN YieldingAlterTableAddColumn( ESchemaCatalog eSchemaCatalog, CRecordInfo *pRecordInfo, const CColumnInfo *pColumnInfo ); + SQLRETURN YieldingAddConstraint( ESchemaCatalog eSchemaCatalog, CRecordInfo *pRecordInfo, const CColumnInfo *pColumnInfo, int nColFlagConstraint ); + SQLRETURN YieldingChangeColumnTypeOrLength( ESchemaCatalog eSchemaCatalog, CRecordInfo *pRecordInfo, const CColumnInfo *pColumnInfoDesired ); + SQLRETURN YieldingChangeColumnProperties( ESchemaCatalog eSchemaCatalog, CRecordInfo *pRecordInfo, const CColumnInfo *pColumnInfoActual, const CColumnInfo *pColumnInfoDesired ); + SQLRETURN YieldingCreateTrigger( ESchemaCatalog eSchemaCatalog, CTriggerInfo &refTriggerInfo ); + SQLRETURN YieldingDropTrigger( ESchemaCatalog eSchemaCatalog, CTriggerInfo &refTriggerInfo ); + + CUtlMap<int,EGCSQLType> m_mapSQLTypeToEType; + int m_iTableCount; + + EConversionMode m_eConversionMode; + + CUtlString m_sRecommendedSQL; + CUtlString m_sDataDestroyingCleanupSQL; + void AddDataDestroyingConversion( const char *pszSQL, const char *pszComment ); + SQLRETURN YieldingProcessUnsafeConversion( ESchemaCatalog eSchemaCatalog, const char *pszSQL, const char *pszComment = NULL ); +}; + + +} // namespace GCSDK +#endif // UPDATESCHEMA_H diff --git a/public/gcsdk/sqlaccess/sqlaccess.h b/public/gcsdk/sqlaccess/sqlaccess.h new file mode 100644 index 0000000..2ea576b --- /dev/null +++ b/public/gcsdk/sqlaccess/sqlaccess.h @@ -0,0 +1,290 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Provides access to SQL at a high level +// +//============================================================================= + +#ifndef SQLACCESS_H +#define SQLACCESS_H +#ifdef _WIN32 +#pragma once +#endif + +#include "gcsdk/gcsqlquery.h" +#include <functional> + +#include "tier0/memdbgon.h" + +namespace GCSDK +{ +class CGCSQLQuery; +class CGCSQLQueryGroup; +class CColumnSet; +class CRecordType; + +//----------------------------------------------------------------------------- +// Purpose: Provides access to SQL at a high level +//----------------------------------------------------------------------------- +class CSQLAccess +{ +public: + CSQLAccess( ESchemaCatalog eSchemaCatalog = k_ESchemaCatalogMain ); + ~CSQLAccess( ); + + bool BBeginTransaction( const char *pchName ); + MUST_CHECK_RETURN bool BCommitTransaction( bool bAllowEmpty = false ); + void RollbackTransaction(); + bool BInTransaction( ) const { return m_bInTransaction; } + const char *PchTransactionName( ) const; + + // Add a listener to be executed should this transaction successfully complete. Invalid to call if + // !BInTransaction() + // + // NOTE: Listeners must assume they are within a transaction commit scope with relevant locks, and must not yield or + // perform re-entrant actions. + void AddCommitListener( std::function<void (void)> &&listener ); + + // Add a listener to be executed should this transaction be rolled back, explicitly or automatically. Invalid to + // call if !BInTransaction() + // + // NOTE: Listeners must assume they are within a transaction commit scope with relevant locks, and must not yield or + // perform re-entrant actions. + void AddRollbackListener( std::function<void (void)> &&listener ); + + bool BYieldingExecute( const char *pchName, const char *pchSQLCommand, uint32 *pcRowsAffected = NULL, bool bSpewOnError = true ); + bool BYieldingExecuteString( const char *pchName, const char *pchSQLCommand, CFmtStr1024 *psResult, uint32 *pcRowsAffected = NULL ); + bool BYieldingExecuteScalarInt( const char *pchName, const char *pchSQLCommand, int *pnResult, uint32 *pcRowsAffected = NULL ); + bool BYieldingExecuteScalarIntWithDefault( const char *pchName, const char *pchSQLCommand, int *pnResult, int iDefaultValue, uint32 *pcRowsAffected = NULL ); + bool BYieldingExecuteScalarUint32( const char *pchName, const char *pchSQLCommand, uint32 *punResult, uint32 *pcRowsAffected = NULL ); + bool BYieldingExecuteScalarUint32WithDefault( const char *pchName, const char *pchSQLCommand, uint32 *punResult, uint32 unDefaultValue, uint32 *pcRowsAffected = NULL ); + bool BYieldingWipeTable( int iTable ); + + template <typename TReturn, typename TCast> + bool BYieldingExecuteSingleResult( const char *pchName, const char *pchSQLCommand, EGCSQLType eType, TReturn *pResult, uint32 *pcRowsAffected ); + template <typename TReturn, typename TCast> + bool BYieldingExecuteSingleResultWithDefault( const char *pchName, const char *pchSQLCommand, EGCSQLType eType, TReturn *pResult, TReturn defaultValue, uint32 *pcRowsAffected ); + + // manipulating CRecordBase (i.e. CSch...) objects in the database + bool BYieldingInsertRecord( const CRecordBase *pRecordBase ); + bool BYieldingInsertWhenNotMatchedOnPK( CRecordBase *pRecordBase ); + bool BYieldingInsertOrUpdateOnPK( CRecordBase *pRecordBase ); + bool BYieldingInsertWithIdentity( CRecordBase* pRecordBase ); + + /// Locate a *single* record from the table which matches the values of the specified columns. + /// Returns: + /// - k_EResultOK - A single record was found and returned + /// - k_EResultNoMatch - no records found + /// - k_EResultLimitExceeded - More than one record found (false is returned in this case --- use a different function to do the query) + /// - (Other K_EResult codes might be returned for invalid usage, SQL problem, etc) + /// If no order by clause is provided, this assumes a singleton will be returned, and will fail if more than one record meets the criteria, if an order by + /// clause is provided though, this will just return the first element + EResult YieldingReadRecordWithWhereColumns( CRecordBase *pRecord, const CColumnSet & readSet, const CColumnSet & whereSet, const char* pchOrderClause = NULL ); + + template< typename SchClass_t> + bool BYieldingReadRecordsWithWhereClause( CUtlVector< SchClass_t > *pvecRecords, const char *pchWhereClause, const CColumnSet & readSet, const char *pchTopClause = NULL, const char* pchOrderClause = NULL ); + template< typename SchClass_t> + bool BYieldingReadRecordsWithQuery( CUtlVector< SchClass_t > *pvecRecords, const char *sQuery, const CColumnSet & readSet ); + bool BYieldingUpdateRecord( const CRecordBase & record, const CColumnSet & whereColumns, const CColumnSet & updateColumns, const class CSQLOutputParams *pOptionalOutputParams = NULL ); + bool BYieldingUpdateRecords( const CRecordBase & whereRecord, const CColumnSet & whereColumns, const CRecordBase & updateRecord, const CColumnSet & updateColumns, const class CSQLOutputParams *pOptionalOutputParams = NULL ); + bool BYieldingDeleteRecords( const CRecordBase & record, const CColumnSet & whereColumns ); + + //called to handle an update or insert request (an upsert), where if the record exists, it will update the masked fields, otherwise it will insert the full record. All fields should + //be initialized within this record in case of an insert. Note that all of the values will be loaded into a structure named 'Src', so you can reference in the where/update as Src.Column1, etc + bool BYieldingUpdateOrInsert( const CRecordBase& record, const CColumnSet & whereColumns, const CColumnSet & updateColumns, const char* pszWhereClause = NULL, const char* pszUpdateClause = NULL ); + + void AddRecordParameters( const CRecordBase &record, const CColumnSet & columnSet ); + + void AddBindParam( const char *pchValue ); + void AddBindParam( const int16 nValue ); + void AddBindParam( const uint16 uValue ); + void AddBindParam( const int32 nValue ); + void AddBindParam( const uint32 uValue ); + void AddBindParam( const uint64 ulValue ); + void AddBindParam( const uint8 *ubValue, const int cubValue ); + void AddBindParam( const float fValue ); + void AddBindParam( const double dValue ); + void AddBindParamRaw( EGCSQLType eType, const byte *pubData, uint32 cubData ); + void ClearParams(); + IGCSQLResultSetList *GetResults(); + + uint32 GetResultSetCount(); + uint32 GetResultSetRowCount( uint32 unResultSet ); + CSQLRecord GetResultRecord( uint32 unResultSet, uint32 unRow ); + +private: + enum EReadSingleResultResult + { + eReadSingle_Error, // something went wrong in the DB or the data was in a format we didn't expect + eReadSingle_ResultFound, // we found a single result and copied the value -- all is well! + eReadSingle_UseDefault, // we didn't find any results but we specified a value in advance for this case + }; + + EReadSingleResultResult BYieldingExecuteSingleResultDataInternal( const char *pchName, const char *pchSQLCommand, EGCSQLType eType, uint8 **pubData, uint32 *punSize, uint32 *pcRowsAffected, bool bHasDefaultValue ); + +private: + void RunListeners_Commit(); + void RunListeners_Rollback(); + CGCSQLQuery *CurrentQuery(); + ESchemaCatalog m_eSchemaCatalog; + CGCSQLQuery *m_pCurrentQuery; + CGCSQLQueryGroup *m_pQueryGroup; + bool m_bInTransaction; + std::vector< std::function<void (void)> > m_vecCommitListeners; + std::vector< std::function<void (void)> > m_vecRollbackListeners; +}; + +//this class will build up and cache an update or insert query. These can be pretty long and expensive to generate, so the intent of this object is to make a static version which can then +//be submitted to various SQL operations +class CSQLUpdateOrInsert +{ +public: + //builds up a query that will insert or merge. The optional clauses can be provided, and if need to reference existing tables, S=Source (new one being added) D=Destination (table being updated) + //NOTE: You cannot use ?'s in the clauses since the parameters are reset and filled in with the provided object + //NOTE: update columns can be empty along with a NULL update clause to simply do an 'insert if it doesn't exist' command + CSQLUpdateOrInsert( const char* pszName, int nTable, const CColumnSet & whereColumns, const CColumnSet & updateColumns, const char* pszWhereClause = NULL, const char* pszUpdateClause = NULL ); + + //called to execute the query on the provided record. Note that this will clear any previously set parameters for the query so it can fill in the provided record + bool BYieldingExecute( CSQLAccess& sqlAccess, const CRecordBase& record, uint32 *out_punRowsAffected = NULL ) const; + +private: + //the display name for our query + CUtlString m_sName; + //the formatted query to run + CUtlString m_sQuery; + //which table we were compiled against to ensure we aren't used on the wrong objects + int m_nTable; +}; + +#define FOR_EACH_SQL_RESULT( sqlAccess, resultSet, record ) \ + for( CSQLRecord record = (sqlAccess).GetResultRecord( resultSet, 0 ); record.IsValid(); record.NextRow() ) + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CSQLOutputParams +{ +public: + CSQLOutputParams( const CRecordBase *pRecord, const CColumnSet& ColumnSet ) + : m_pRecord( pRecord ) + , m_ColumnSet( ColumnSet ) + { + Assert( pRecord ); + Assert( pRecord->GetPRecordInfo() == ColumnSet.GetRecordInfo() ); + } + + const CRecordBase& GetRecord() const { return *m_pRecord; } + const CColumnSet& GetColumnSet() const { return m_ColumnSet; } + +private: + const CRecordBase *m_pRecord; + const CColumnSet& m_ColumnSet; +}; + +//----------------------------------------------------------------------------- +// Purpose: templatized version of querying for a single value +//----------------------------------------------------------------------------- +template <typename TReturn, typename TCast> +bool CSQLAccess::BYieldingExecuteSingleResult( const char *pchName, const char *pchSQLCommand, EGCSQLType eType, TReturn *pResult, uint32 *pcRowsAffected ) +{ + uint8 *pubData; + uint32 cubData; + if( CSQLAccess::BYieldingExecuteSingleResultDataInternal( pchName, pchSQLCommand, eType, &pubData, &cubData, pcRowsAffected, false ) != eReadSingle_ResultFound ) + return false; + + *pResult = *( (TCast *)pubData ); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: templatized version of querying for a single value +//----------------------------------------------------------------------------- +template <typename TReturn, typename TCast> +bool CSQLAccess::BYieldingExecuteSingleResultWithDefault( const char *pchName, const char *pchSQLCommand, EGCSQLType eType, TReturn *pResult, TReturn defaultValue, uint32 *pcRowsAffected ) +{ + uint8 *pubData; + uint32 cubData; + EReadSingleResultResult eResult = CSQLAccess::BYieldingExecuteSingleResultDataInternal( pchName, pchSQLCommand, eType, &pubData, &cubData, pcRowsAffected, true ); + + if ( eResult == eReadSingle_Error ) + return false; + + if ( eResult == eReadSingle_ResultFound ) + { + *pResult = *( (TCast *)pubData ); + } + else + { + Assert( eResult == eReadSingle_UseDefault ); + *pResult = defaultValue; + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Reads a list of records from the DB according to the specified where +// clause +// Input: pRecordBase - record to insert +// Output: true if successful, false otherwise +//----------------------------------------------------------------------------- +template< typename SchClass_t> +bool CSQLAccess::BYieldingReadRecordsWithWhereClause( CUtlVector< SchClass_t > *pvecRecords, const char *pchWhereClause, const CColumnSet & readSet, const char *pchTopClause, const char* pchOrderClause ) +{ + AssertMsg( !BInTransaction(), "BYieldingReadRecordsWithWhereClause is not supported in a transaction" ); + if( BInTransaction() ) + return false; + + Assert( !readSet.IsEmpty() ); + TSQLCmdStr sStatement; + BuildSelectStatementText( &sStatement, readSet, pchTopClause ); + + //finished our select, so add our where filter if provided + if( pchWhereClause ) + { + sStatement.Append( " WHERE " ); + sStatement.Append( pchWhereClause ); + } + //finished our where, so add our order by clause if provided + if( pchOrderClause ) + { + sStatement.Append( " ORDER BY " ); + sStatement.Append( pchOrderClause ); + } + + return BYieldingReadRecordsWithQuery< SchClass_t >( pvecRecords, sStatement, readSet ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Inserts a new record into the DB and reads non-insertable fields back +// into the record. +// Input: pRecordBase - record to insert +// Output: true if successful, false otherwise +//----------------------------------------------------------------------------- +template< typename SchClass_t> +bool CSQLAccess::BYieldingReadRecordsWithQuery( CUtlVector< SchClass_t > *pvecRecords, const char *sQuery, const CColumnSet & readSet ) +{ + AssertMsg( !BInTransaction(), "BYieldingReadRecordsWithQuery is not supported in a transaction" ); + if( BInTransaction() ) + return false; + + Assert(!readSet.IsEmpty() ); + if( !BYieldingExecute( NULL, sQuery ) ) + return false; + + Assert( GetResultSetCount() == 1 ); + if ( GetResultSetCount() != 1 ) + return false; + + // make sure the types are the same + IGCSQLResultSet *pResultSet = m_pQueryGroup->GetResults()->GetResultSet( 0 ); + return CopyResultToSchVector( pResultSet, readSet, pvecRecords ); +} + +} // namespace GCSDK + +#include "tier0/memdbgoff.h" + +#endif // SQLACCESS_H diff --git a/public/gcsdk/sqlaccess/sqlrecord.h b/public/gcsdk/sqlaccess/sqlrecord.h new file mode 100644 index 0000000..e0846d0 --- /dev/null +++ b/public/gcsdk/sqlaccess/sqlrecord.h @@ -0,0 +1,54 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//============================================================================= + +#ifndef GCSQLRECORD_H +#define GCSQLRECORD_H + +namespace GCSDK +{ + +class CSQLRecord +{ +public: + CSQLRecord( uint32 unRow, IGCSQLResultSet *pResultSet ); + CSQLRecord( ); + ~CSQLRecord(); + + void Init( uint32 unRow, IGCSQLResultSet *pResultSet ); + + bool BWriteToRecord( CRecordBase *pRecord, const CColumnSet & csWriteFields ); + bool BGetColumnData( uint32 unColumn, uint8 **ppubField, int *pcubField ); + bool BGetColumnData( uint32 unColumn, uint8 **ppubField, size_t *pcubField ); + bool BGetStringValue( uint32 unColumn, const char **ppchVal ); + bool BGetStringValue( uint32 unColumn, CFmtStr1024 *psVal ); + bool BGetIntValue( uint32 unColumn, int *pnVal ); + bool BGetInt16Value( uint32 unColumn, int16 *pnVal ); + bool BGetInt64Value( uint32 unColumn, int64 *puVal ); + bool BGetUint64Value( uint32 unColumn, uint64 *puVal ); + bool BGetByteValue( uint32 unColumn, byte *pVal ); + bool BGetBoolValue( uint32 unColumn, bool *pVal ); + bool BGetUint32Value( uint32 unColumn, uint32 *puVal ); + bool BGetUint16Value( uint32 unColumn, uint16 *puVal ); + bool BGetUint8Value( uint32 unColumn, uint8 *puVal ); + bool BGetFloatValue( uint32 unColumn, float *pfVal ); + bool BGetDoubleValue( uint32 unColumn, double *pdVal ); + + void RenderField( uint32 unColumn, int cchBuffer, char *pchBuffer ); + + bool NextRow(); + bool IsValid() const { return m_pResultSet != NULL; } + +private: + + + bool BValidateColumnIndex( uint32 unColumn ); + IGCSQLResultSet *m_pResultSet; + uint32 m_unRow; +}; + +} // namespace GCSDK +#endif // GCSQLRECORD_H diff --git a/public/gcsdk/sqlaccess/sqlutil.h b/public/gcsdk/sqlaccess/sqlutil.h new file mode 100644 index 0000000..5cad4d3 --- /dev/null +++ b/public/gcsdk/sqlaccess/sqlutil.h @@ -0,0 +1,113 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//============================================================================= + +#ifndef SQLUTIL_H +#define SQLUTIL_H + +#ifdef _WIN32 +#pragma once +#endif + + +namespace GCSDK +{ +typedef CFmtStrN< 4096 > TSQLCmdStr; + +// Returns a long (1024 char) string of "?,?,?,?,?"... for use in IN clauses or INSERT statements +const char *GetInsertArgString(); +// Returns the number of characters that should be used for the specified number of parameters +uint32 GetInsertArgStringChars( uint32 nNumParams ); +// Returns the maximum number of parameters that are supported by the GetInsertArgString string +uint32 GetInsertArgStringMaxParams(); + + +void ConvertFieldToText( EGCSQLType eFieldType, uint8 *pubRecord, int cubRecord, char *rgchField, int cchField, bool bQuoteString = true ); +void ConvertFieldArrayToInText( const CColumnInfo &columnInfo, byte *pubData, int cubData, char *rgchResult, bool bForPreparedStatement ); +char *SQLTypeFromField( const CColumnInfo &colInfo, char *pchBuf, int cchBuf ); +void EscapeStringValue( char *rgchField, int cchField ); +void AppendConstraints( const CRecordInfo *pRecordInfo, const CColumnInfo *pColumnInfo, bool bForAdd, CFmtStrMax & sCmd ); +void AppendTableConstraints( CRecordInfo *pRecordInfo, CFmtStrMax & sCmd ); +void AppendConstraint( const char *pchTableName, const char *pchColumnName, int nColFlagConstraint, bool bForAdd, bool bClustered, + CFmtStrMax & sCmd, int nFillFactor ); +void BuildTablePKConstraintText( TSQLCmdStr *psStatement, CRecordInfo *pRecordInfo ); +//void BuildSelectStatementText( CUtlVector<CQuery> *pVecQuery, bool bForPreparedStatement, char *pchStatement, int cchStatement ); + +void BuildInsertStatementText( TSQLCmdStr *psStatement, const CRecordInfo *pRecordInfo ); +void BuildInsertAndReadStatementText( TSQLCmdStr *psStatement, CUtlVector<int> *pvecOutputFields, const CRecordInfo *pRecordInfo ) ; +void BuildMergeStatementTextOnPKWhenMatchedUpdateWhenNotMatchedInsert( TSQLCmdStr *psStatement, const CRecordInfo *pRecordInfo ); +void BuildMergeStatementTextOnPKWhenNotMatchedInsert( TSQLCmdStr *psStatement, const CRecordInfo *pRecordInfo ); +void BuildSelectStatementText( TSQLCmdStr *psStatement, const CColumnSet & selectSet, const char *pchTopClause = NULL ); +void BuildUpdateStatementText( TSQLCmdStr *psStatement, const CColumnSet & columnSet ); +void BuildDeleteStatementText( TSQLCmdStr *psStatement, const CRecordInfo* pRecordInfo ); +void AppendWhereClauseText( TSQLCmdStr *psClause, const CColumnSet & columnSet ); +void BuildOutputClauseText( TSQLCmdStr *psClause, const CColumnSet & columnSet ); + +template< typename T > +bool CopyResultToSchVector( IGCSQLResultSet *pResultSet, const CColumnSet & readSet, CUtlVector< T > *pvecRecords ) +{ + if ( pResultSet->GetRowCount() == 0 ) + return true; + + FOR_EACH_COLUMN_IN_SET( readSet, nColumnIndex ) + { + EGCSQLType eRecordType = readSet.GetColumnInfo( nColumnIndex ).GetType(); + EGCSQLType eResultType = pResultSet->GetColumnType( nColumnIndex ); + + Assert( eResultType == eRecordType ); + if( eRecordType != eResultType ) + return false; + } + + + for( CSQLRecord sqlRecord( 0, pResultSet ); sqlRecord.IsValid(); sqlRecord.NextRow() ) + { + int nRecord = pvecRecords->AddToTail(); + + FOR_EACH_COLUMN_IN_SET( readSet, nColumnIndex ) + { + uint8 *pubData; + uint32 cubData; + + DbgVerify( sqlRecord.BGetColumnData( nColumnIndex, &pubData, (int*)&cubData ) ); + DbgVerify( pvecRecords->Element( nRecord ).BSetField( readSet.GetColumn( nColumnIndex), pubData, cubData ) ); + } + } + return true; +} + +//EResult UpdateOrInsertUnique( CSQLAccess &sqlAccess, int iTable, int iField, CRecordBase *pRecordBase, int iIndexID ); +//bool BIsDuplicateInsertAttempt( const CSQLErrorInfo *pErr ); + + +#define EXIT_WITH_SQL_FAILURE( ret ) { nRet = ret; goto Exit; } + +#define EXIT_ON_BOOL_FAILURE( bRet, msg ) \ + { \ + if ( false == bRet ) \ + { \ + SetSQLError( msg ); \ + nRet = SQL_ERROR; \ + goto Exit; \ + } \ + } + +#define SAFE_CLOSE_STMT( x ) \ + if ( NULL != (x) ) \ + { \ + SQLFreeHandle( SQL_HANDLE_STMT, (x) ); \ + (x) = NULL; \ + } + +#define SAFE_FREE_HANDLE( x, y ) \ + if ( NULL != (x) ) \ +{ \ + SQLFreeHandle( y, (x) ); \ + (x) = NULL; \ +} + +} // namespace GCSDK +#endif // SQLUTIL_H |