Team Fortress 2 Source Code as on 22/4/2020
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.

290 lines
13 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Provides access to SQL at a high level
  4. //
  5. //=============================================================================
  6. #ifndef SQLACCESS_H
  7. #define SQLACCESS_H
  8. #ifdef _WIN32
  9. #pragma once
  10. #endif
  11. #include "gcsdk/gcsqlquery.h"
  12. #include <functional>
  13. #include "tier0/memdbgon.h"
  14. namespace GCSDK
  15. {
  16. class CGCSQLQuery;
  17. class CGCSQLQueryGroup;
  18. class CColumnSet;
  19. class CRecordType;
  20. //-----------------------------------------------------------------------------
  21. // Purpose: Provides access to SQL at a high level
  22. //-----------------------------------------------------------------------------
  23. class CSQLAccess
  24. {
  25. public:
  26. CSQLAccess( ESchemaCatalog eSchemaCatalog = k_ESchemaCatalogMain );
  27. ~CSQLAccess( );
  28. bool BBeginTransaction( const char *pchName );
  29. MUST_CHECK_RETURN bool BCommitTransaction( bool bAllowEmpty = false );
  30. void RollbackTransaction();
  31. bool BInTransaction( ) const { return m_bInTransaction; }
  32. const char *PchTransactionName( ) const;
  33. // Add a listener to be executed should this transaction successfully complete. Invalid to call if
  34. // !BInTransaction()
  35. //
  36. // NOTE: Listeners must assume they are within a transaction commit scope with relevant locks, and must not yield or
  37. // perform re-entrant actions.
  38. void AddCommitListener( std::function<void (void)> &&listener );
  39. // Add a listener to be executed should this transaction be rolled back, explicitly or automatically. Invalid to
  40. // call if !BInTransaction()
  41. //
  42. // NOTE: Listeners must assume they are within a transaction commit scope with relevant locks, and must not yield or
  43. // perform re-entrant actions.
  44. void AddRollbackListener( std::function<void (void)> &&listener );
  45. bool BYieldingExecute( const char *pchName, const char *pchSQLCommand, uint32 *pcRowsAffected = NULL, bool bSpewOnError = true );
  46. bool BYieldingExecuteString( const char *pchName, const char *pchSQLCommand, CFmtStr1024 *psResult, uint32 *pcRowsAffected = NULL );
  47. bool BYieldingExecuteScalarInt( const char *pchName, const char *pchSQLCommand, int *pnResult, uint32 *pcRowsAffected = NULL );
  48. bool BYieldingExecuteScalarIntWithDefault( const char *pchName, const char *pchSQLCommand, int *pnResult, int iDefaultValue, uint32 *pcRowsAffected = NULL );
  49. bool BYieldingExecuteScalarUint32( const char *pchName, const char *pchSQLCommand, uint32 *punResult, uint32 *pcRowsAffected = NULL );
  50. bool BYieldingExecuteScalarUint32WithDefault( const char *pchName, const char *pchSQLCommand, uint32 *punResult, uint32 unDefaultValue, uint32 *pcRowsAffected = NULL );
  51. bool BYieldingWipeTable( int iTable );
  52. template <typename TReturn, typename TCast>
  53. bool BYieldingExecuteSingleResult( const char *pchName, const char *pchSQLCommand, EGCSQLType eType, TReturn *pResult, uint32 *pcRowsAffected );
  54. template <typename TReturn, typename TCast>
  55. bool BYieldingExecuteSingleResultWithDefault( const char *pchName, const char *pchSQLCommand, EGCSQLType eType, TReturn *pResult, TReturn defaultValue, uint32 *pcRowsAffected );
  56. // manipulating CRecordBase (i.e. CSch...) objects in the database
  57. bool BYieldingInsertRecord( const CRecordBase *pRecordBase );
  58. bool BYieldingInsertWhenNotMatchedOnPK( CRecordBase *pRecordBase );
  59. bool BYieldingInsertOrUpdateOnPK( CRecordBase *pRecordBase );
  60. bool BYieldingInsertWithIdentity( CRecordBase* pRecordBase );
  61. /// Locate a *single* record from the table which matches the values of the specified columns.
  62. /// Returns:
  63. /// - k_EResultOK - A single record was found and returned
  64. /// - k_EResultNoMatch - no records found
  65. /// - k_EResultLimitExceeded - More than one record found (false is returned in this case --- use a different function to do the query)
  66. /// - (Other K_EResult codes might be returned for invalid usage, SQL problem, etc)
  67. /// 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
  68. /// clause is provided though, this will just return the first element
  69. EResult YieldingReadRecordWithWhereColumns( CRecordBase *pRecord, const CColumnSet & readSet, const CColumnSet & whereSet, const char* pchOrderClause = NULL );
  70. template< typename SchClass_t>
  71. bool BYieldingReadRecordsWithWhereClause( CUtlVector< SchClass_t > *pvecRecords, const char *pchWhereClause, const CColumnSet & readSet, const char *pchTopClause = NULL, const char* pchOrderClause = NULL );
  72. template< typename SchClass_t>
  73. bool BYieldingReadRecordsWithQuery( CUtlVector< SchClass_t > *pvecRecords, const char *sQuery, const CColumnSet & readSet );
  74. bool BYieldingUpdateRecord( const CRecordBase & record, const CColumnSet & whereColumns, const CColumnSet & updateColumns, const class CSQLOutputParams *pOptionalOutputParams = NULL );
  75. bool BYieldingUpdateRecords( const CRecordBase & whereRecord, const CColumnSet & whereColumns, const CRecordBase & updateRecord, const CColumnSet & updateColumns, const class CSQLOutputParams *pOptionalOutputParams = NULL );
  76. bool BYieldingDeleteRecords( const CRecordBase & record, const CColumnSet & whereColumns );
  77. //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
  78. //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
  79. bool BYieldingUpdateOrInsert( const CRecordBase& record, const CColumnSet & whereColumns, const CColumnSet & updateColumns, const char* pszWhereClause = NULL, const char* pszUpdateClause = NULL );
  80. void AddRecordParameters( const CRecordBase &record, const CColumnSet & columnSet );
  81. void AddBindParam( const char *pchValue );
  82. void AddBindParam( const int16 nValue );
  83. void AddBindParam( const uint16 uValue );
  84. void AddBindParam( const int32 nValue );
  85. void AddBindParam( const uint32 uValue );
  86. void AddBindParam( const uint64 ulValue );
  87. void AddBindParam( const uint8 *ubValue, const int cubValue );
  88. void AddBindParam( const float fValue );
  89. void AddBindParam( const double dValue );
  90. void AddBindParamRaw( EGCSQLType eType, const byte *pubData, uint32 cubData );
  91. void ClearParams();
  92. IGCSQLResultSetList *GetResults();
  93. uint32 GetResultSetCount();
  94. uint32 GetResultSetRowCount( uint32 unResultSet );
  95. CSQLRecord GetResultRecord( uint32 unResultSet, uint32 unRow );
  96. private:
  97. enum EReadSingleResultResult
  98. {
  99. eReadSingle_Error, // something went wrong in the DB or the data was in a format we didn't expect
  100. eReadSingle_ResultFound, // we found a single result and copied the value -- all is well!
  101. eReadSingle_UseDefault, // we didn't find any results but we specified a value in advance for this case
  102. };
  103. EReadSingleResultResult BYieldingExecuteSingleResultDataInternal( const char *pchName, const char *pchSQLCommand, EGCSQLType eType, uint8 **pubData, uint32 *punSize, uint32 *pcRowsAffected, bool bHasDefaultValue );
  104. private:
  105. void RunListeners_Commit();
  106. void RunListeners_Rollback();
  107. CGCSQLQuery *CurrentQuery();
  108. ESchemaCatalog m_eSchemaCatalog;
  109. CGCSQLQuery *m_pCurrentQuery;
  110. CGCSQLQueryGroup *m_pQueryGroup;
  111. bool m_bInTransaction;
  112. std::vector< std::function<void (void)> > m_vecCommitListeners;
  113. std::vector< std::function<void (void)> > m_vecRollbackListeners;
  114. };
  115. //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
  116. //be submitted to various SQL operations
  117. class CSQLUpdateOrInsert
  118. {
  119. public:
  120. //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)
  121. //NOTE: You cannot use ?'s in the clauses since the parameters are reset and filled in with the provided object
  122. //NOTE: update columns can be empty along with a NULL update clause to simply do an 'insert if it doesn't exist' command
  123. CSQLUpdateOrInsert( const char* pszName, int nTable, const CColumnSet & whereColumns, const CColumnSet & updateColumns, const char* pszWhereClause = NULL, const char* pszUpdateClause = NULL );
  124. //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
  125. bool BYieldingExecute( CSQLAccess& sqlAccess, const CRecordBase& record, uint32 *out_punRowsAffected = NULL ) const;
  126. private:
  127. //the display name for our query
  128. CUtlString m_sName;
  129. //the formatted query to run
  130. CUtlString m_sQuery;
  131. //which table we were compiled against to ensure we aren't used on the wrong objects
  132. int m_nTable;
  133. };
  134. #define FOR_EACH_SQL_RESULT( sqlAccess, resultSet, record ) \
  135. for( CSQLRecord record = (sqlAccess).GetResultRecord( resultSet, 0 ); record.IsValid(); record.NextRow() )
  136. //-----------------------------------------------------------------------------
  137. // Purpose:
  138. //-----------------------------------------------------------------------------
  139. class CSQLOutputParams
  140. {
  141. public:
  142. CSQLOutputParams( const CRecordBase *pRecord, const CColumnSet& ColumnSet )
  143. : m_pRecord( pRecord )
  144. , m_ColumnSet( ColumnSet )
  145. {
  146. Assert( pRecord );
  147. Assert( pRecord->GetPRecordInfo() == ColumnSet.GetRecordInfo() );
  148. }
  149. const CRecordBase& GetRecord() const { return *m_pRecord; }
  150. const CColumnSet& GetColumnSet() const { return m_ColumnSet; }
  151. private:
  152. const CRecordBase *m_pRecord;
  153. const CColumnSet& m_ColumnSet;
  154. };
  155. //-----------------------------------------------------------------------------
  156. // Purpose: templatized version of querying for a single value
  157. //-----------------------------------------------------------------------------
  158. template <typename TReturn, typename TCast>
  159. bool CSQLAccess::BYieldingExecuteSingleResult( const char *pchName, const char *pchSQLCommand, EGCSQLType eType, TReturn *pResult, uint32 *pcRowsAffected )
  160. {
  161. uint8 *pubData;
  162. uint32 cubData;
  163. if( CSQLAccess::BYieldingExecuteSingleResultDataInternal( pchName, pchSQLCommand, eType, &pubData, &cubData, pcRowsAffected, false ) != eReadSingle_ResultFound )
  164. return false;
  165. *pResult = *( (TCast *)pubData );
  166. return true;
  167. }
  168. //-----------------------------------------------------------------------------
  169. // Purpose: templatized version of querying for a single value
  170. //-----------------------------------------------------------------------------
  171. template <typename TReturn, typename TCast>
  172. bool CSQLAccess::BYieldingExecuteSingleResultWithDefault( const char *pchName, const char *pchSQLCommand, EGCSQLType eType, TReturn *pResult, TReturn defaultValue, uint32 *pcRowsAffected )
  173. {
  174. uint8 *pubData;
  175. uint32 cubData;
  176. EReadSingleResultResult eResult = CSQLAccess::BYieldingExecuteSingleResultDataInternal( pchName, pchSQLCommand, eType, &pubData, &cubData, pcRowsAffected, true );
  177. if ( eResult == eReadSingle_Error )
  178. return false;
  179. if ( eResult == eReadSingle_ResultFound )
  180. {
  181. *pResult = *( (TCast *)pubData );
  182. }
  183. else
  184. {
  185. Assert( eResult == eReadSingle_UseDefault );
  186. *pResult = defaultValue;
  187. }
  188. return true;
  189. }
  190. //-----------------------------------------------------------------------------
  191. // Purpose: Reads a list of records from the DB according to the specified where
  192. // clause
  193. // Input: pRecordBase - record to insert
  194. // Output: true if successful, false otherwise
  195. //-----------------------------------------------------------------------------
  196. template< typename SchClass_t>
  197. bool CSQLAccess::BYieldingReadRecordsWithWhereClause( CUtlVector< SchClass_t > *pvecRecords, const char *pchWhereClause, const CColumnSet & readSet, const char *pchTopClause, const char* pchOrderClause )
  198. {
  199. AssertMsg( !BInTransaction(), "BYieldingReadRecordsWithWhereClause is not supported in a transaction" );
  200. if( BInTransaction() )
  201. return false;
  202. Assert( !readSet.IsEmpty() );
  203. TSQLCmdStr sStatement;
  204. BuildSelectStatementText( &sStatement, readSet, pchTopClause );
  205. //finished our select, so add our where filter if provided
  206. if( pchWhereClause )
  207. {
  208. sStatement.Append( " WHERE " );
  209. sStatement.Append( pchWhereClause );
  210. }
  211. //finished our where, so add our order by clause if provided
  212. if( pchOrderClause )
  213. {
  214. sStatement.Append( " ORDER BY " );
  215. sStatement.Append( pchOrderClause );
  216. }
  217. return BYieldingReadRecordsWithQuery< SchClass_t >( pvecRecords, sStatement, readSet );
  218. }
  219. //-----------------------------------------------------------------------------
  220. // Purpose: Inserts a new record into the DB and reads non-insertable fields back
  221. // into the record.
  222. // Input: pRecordBase - record to insert
  223. // Output: true if successful, false otherwise
  224. //-----------------------------------------------------------------------------
  225. template< typename SchClass_t>
  226. bool CSQLAccess::BYieldingReadRecordsWithQuery( CUtlVector< SchClass_t > *pvecRecords, const char *sQuery, const CColumnSet & readSet )
  227. {
  228. AssertMsg( !BInTransaction(), "BYieldingReadRecordsWithQuery is not supported in a transaction" );
  229. if( BInTransaction() )
  230. return false;
  231. Assert(!readSet.IsEmpty() );
  232. if( !BYieldingExecute( NULL, sQuery ) )
  233. return false;
  234. Assert( GetResultSetCount() == 1 );
  235. if ( GetResultSetCount() != 1 )
  236. return false;
  237. // make sure the types are the same
  238. IGCSQLResultSet *pResultSet = m_pQueryGroup->GetResults()->GetResultSet( 0 );
  239. return CopyResultToSchVector( pResultSet, readSet, pvecRecords );
  240. }
  241. } // namespace GCSDK
  242. #include "tier0/memdbgoff.h"
  243. #endif // SQLACCESS_H