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.

543 lines
16 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================
  7. //#include "misc.h"
  8. #include "isqlwrapper.h"
  9. #include "time.h"
  10. #include "sqlhelpers.h"
  11. #include "tier0/memdbgon.h"
  12. #pragma warning( disable : 4706 ) // Warning C4706: assignment within conditional expression
  13. //-----------------------------------------------------------------------------
  14. // Purpose: converts between a MySQL field type and our column types
  15. //-----------------------------------------------------------------------------
  16. EColumnType GetEColumnTypeFromESQLFieldType( ESQLFieldType eSQLFieldType )
  17. {
  18. switch( eSQLFieldType )
  19. {
  20. case FIELD_TYPE_DECIMAL:
  21. case FIELD_TYPE_TINY:
  22. case FIELD_TYPE_SHORT:
  23. case FIELD_TYPE_LONG:
  24. return SQL_INT;
  25. break;
  26. case FIELD_TYPE_FLOAT:
  27. case FIELD_TYPE_DOUBLE:
  28. return SQL_FLOAT;
  29. break;
  30. case FIELD_TYPE_LONGLONG:
  31. return SQL_UINT64;
  32. break;
  33. case FIELD_TYPE_NULL:
  34. case FIELD_TYPE_INT24:
  35. return SQL_NONE;
  36. break;
  37. case FIELD_TYPE_DATE:
  38. case FIELD_TYPE_TIMESTAMP:
  39. case FIELD_TYPE_TIME:
  40. case FIELD_TYPE_DATETIME:
  41. case FIELD_TYPE_YEAR:
  42. case FIELD_TYPE_NEWDATE:
  43. return SQL_TIME;
  44. break;
  45. case FIELD_TYPE_ENUM:
  46. case FIELD_TYPE_SET:
  47. case FIELD_TYPE_TINY_BLOB:
  48. case FIELD_TYPE_MEDIUM_BLOB:
  49. case FIELD_TYPE_LONG_BLOB:
  50. case FIELD_TYPE_BLOB:
  51. return SQL_NONE;
  52. break;
  53. case FIELD_TYPE_VAR_STRING:
  54. case FIELD_TYPE_STRING:
  55. return SQL_STRING;
  56. break;
  57. }
  58. return SQL_NONE;
  59. }
  60. //-----------------------------------------------------------------------------
  61. // Purpose: Constructor.
  62. //-----------------------------------------------------------------------------
  63. CSQLColumn::CSQLColumn( const char *pchName, ESQLFieldType eSQLFieldType )
  64. {
  65. Assert( pchName );
  66. Q_strncpy( m_rgchName, pchName, sizeof( m_rgchName ) );
  67. m_ESQLFieldType = eSQLFieldType;
  68. m_EColumnType = GetEColumnTypeFromESQLFieldType( m_ESQLFieldType );
  69. }
  70. //-----------------------------------------------------------------------------
  71. // Purpose: Constructor
  72. //-----------------------------------------------------------------------------
  73. CSQLTable::CSQLTable( const char *pchName )
  74. {
  75. Assert( pchName );
  76. Q_strncpy( m_rgchName, pchName, sizeof( m_rgchName ) );
  77. }
  78. //-----------------------------------------------------------------------------
  79. // Purpose: copy constructor (used by CUtlVector)
  80. //-----------------------------------------------------------------------------
  81. CSQLTable::CSQLTable( CSQLTable const &sqlTable )
  82. {
  83. Q_strncpy( m_rgchName, sqlTable.m_rgchName, sizeof( m_rgchName ) );
  84. for ( int iSQLColumn = 0; iSQLColumn < sqlTable.m_VecSQLColumn.Count(); iSQLColumn++ )
  85. {
  86. m_VecSQLColumn.AddToTail( CSQLColumn( sqlTable.m_VecSQLColumn[iSQLColumn].PchColumnName(),
  87. sqlTable.m_VecSQLColumn[iSQLColumn].GetESQLFieldType() ) );
  88. }
  89. }
  90. //-----------------------------------------------------------------------------
  91. // Purpose: add a new column to this data description
  92. //-----------------------------------------------------------------------------
  93. void CSQLTable::AddColumn( const char *pchName, ESQLFieldType eSQLFieldType )
  94. {
  95. m_VecSQLColumn.AddToTail( CSQLColumn( pchName, eSQLFieldType ) );
  96. }
  97. //-----------------------------------------------------------------------------
  98. // Purpose: Ensure that all of our internal structures are consistent, and
  99. // account for all memory that we've allocated.
  100. // Input: validator - Our global validator object
  101. // pchName - Our name (typically a member var in our container)
  102. //-----------------------------------------------------------------------------
  103. #ifdef DBGFLAG_VALIDATE
  104. void CSQLTable::Validate( CValidator &validator, char *pchName )
  105. {
  106. validator.Push( "CSQLTable", this, pchName );
  107. m_VecSQLColumn.Validate( validator, "m_VecSQLColumn" );
  108. validator.Pop();
  109. }
  110. #endif
  111. //-----------------------------------------------------------------------------
  112. // Purpose: Create the table descriptions from a data base
  113. //-----------------------------------------------------------------------------
  114. bool CSQLTableSet::Init( ISQLHelper *pSQLHelper )
  115. {
  116. m_VecSQLTable.RemoveAll();
  117. MYSQL_RES *pMySQLRes = NULL;
  118. bool bRet = pSQLHelper->BInternalQuery( "show tables", &pMySQLRes );
  119. if ( !bRet || !pMySQLRes )
  120. {
  121. return false;
  122. }
  123. MYSQL_ROW row;
  124. while ( ( row = mysql_fetch_row( pMySQLRes ) ) )
  125. {
  126. CSQLTable sqlTable( row[0] );
  127. m_VecSQLTable.AddToTail( sqlTable );
  128. }
  129. mysql_free_result( pMySQLRes );
  130. for ( int i = 0; i < m_VecSQLTable.Count(); i++ )
  131. {
  132. CSQLTable &sqlTable = m_VecSQLTable[i];
  133. char szQuery[512];
  134. Q_snprintf( szQuery, sizeof(szQuery), "select * from %s limit 1", sqlTable.PchName() );
  135. MYSQL_RES *pMySQLRes = NULL;
  136. bool bRet = pSQLHelper->BInternalQuery( szQuery, &pMySQLRes );
  137. if ( !bRet || !pMySQLRes )
  138. {
  139. m_VecSQLTable.RemoveAll();
  140. return false;
  141. }
  142. uint nFields = mysql_num_fields( pMySQLRes );
  143. Assert( nFields > 0 );
  144. MYSQL_FIELD *pFields = mysql_fetch_fields( pMySQLRes );
  145. for( uint i = 0; i < nFields; i++)
  146. {
  147. sqlTable.AddColumn( pFields[i].name, pFields[i].type );
  148. }
  149. mysql_free_result( pMySQLRes );
  150. }
  151. m_bInit = true;
  152. return true;
  153. }
  154. //-----------------------------------------------------------------------------
  155. // Purpose: returns a pointer to each table in the db
  156. //-----------------------------------------------------------------------------
  157. const ISQLTable *CSQLTableSet::PSQLTable( int iSQLTable ) const
  158. {
  159. return &m_VecSQLTable[iSQLTable];
  160. }
  161. //-----------------------------------------------------------------------------
  162. // Purpose: Ensure that all of our internal structures are consistent, and
  163. // account for all memory that we've allocated.
  164. // Input: validator - Our global validator object
  165. // pchName - Our name (typically a member var in our container)
  166. //-----------------------------------------------------------------------------
  167. #ifdef DBGFLAG_VALIDATE
  168. void CSQLTableSet::Validate( CValidator &validator, char *pchName )
  169. {
  170. validator.Push( "CSQLTableSet", this, pchName );
  171. m_VecSQLTable.Validate( validator, "m_VecSQLTable" );
  172. for ( int i = 0; i < m_VecSQLTable.Count(); i++ )
  173. {
  174. m_VecSQLTable[i].Validate( validator, "m_VecSQLTable[i]" );
  175. }
  176. validator.Pop();
  177. }
  178. #endif
  179. //-----------------------------------------------------------------------------
  180. // Purpose: constructor
  181. //-----------------------------------------------------------------------------
  182. CSQLRow::CSQLRow( MYSQL_ROW *pMySQLRow, const ISQLTable *pSQLTableDescription, int cRowData )
  183. {
  184. Assert( pMySQLRow && pSQLTableDescription );
  185. for ( int iRowData = 0; iRowData < cRowData; iRowData++ )
  186. {
  187. struct SQLRowData_s sqlRowData;
  188. sqlRowData.eColumnType = pSQLTableDescription->GetEColumnType( iRowData );
  189. const CSQLTable *tableDesc = static_cast<const CSQLTable *>(pSQLTableDescription);
  190. switch ( tableDesc->GetESQLFieldType( iRowData ) )
  191. {
  192. case FIELD_TYPE_TINY:
  193. case FIELD_TYPE_DECIMAL:
  194. case FIELD_TYPE_SHORT:
  195. case FIELD_TYPE_LONG:
  196. sqlRowData.data.nData = atoi( ( *pMySQLRow ) [iRowData] );
  197. break;
  198. case FIELD_TYPE_FLOAT:
  199. case FIELD_TYPE_DOUBLE:
  200. sqlRowData.data.flData = atof( ( *pMySQLRow ) [iRowData] );
  201. break;
  202. case FIELD_TYPE_NULL:
  203. case FIELD_TYPE_INT24:
  204. memset( &sqlRowData.data, 0x0, sizeof( sqlRowData.data ) );
  205. break;
  206. case FIELD_TYPE_LONGLONG:
  207. sqlRowData.data.nData = atoi( ( *pMySQLRow ) [iRowData] );
  208. break;
  209. case FIELD_TYPE_TIMESTAMP:
  210. case FIELD_TYPE_DATETIME:
  211. {
  212. struct tm tm;
  213. char num[5];
  214. char szValue[64];
  215. Q_memset( &tm, 0x0, sizeof( tm ) );
  216. Q_strncpy( szValue, ( *pMySQLRow )[iRowData], sizeof( szValue ) );
  217. const char *str= szValue;
  218. num[0] =*str++; num[1] =*str++; num[2] =*str++; num[3] =*str++; num[4] = 0;
  219. tm.tm_year = strtol( num, 0, 10 ) - 1900;
  220. if (*str == '-') str++;
  221. num[0] = *str++; num[1] = *str++; num[2] = 0;
  222. tm.tm_mon = strtol( num, 0, 10 ) - 1;
  223. if (*str == '-') str++;
  224. num[0] = *str++; num[1] = *str++; num[2] = 0;
  225. tm.tm_mday = strtol( num, 0, 10 );
  226. num[0] = *str++; num[1] = *str++; num[2] = 0;
  227. tm.tm_hour = strtol( num, 0, 10 );
  228. if (*str == ':') str++;
  229. num[0] = *str++; num[1] = *str++; num[2] = 0;
  230. tm.tm_min = strtol( num, 0, 10 );
  231. if (*str == ':') str++;
  232. num[0] = *str++; num[1] = *str++; num[2] = 0;
  233. tm.tm_sec = strtol( num, 0, 10 );
  234. sqlRowData.data.ulTime = _mktime64( &tm );
  235. }
  236. break;
  237. case FIELD_TYPE_TIME:
  238. case FIELD_TYPE_DATE:
  239. case FIELD_TYPE_YEAR:
  240. case FIELD_TYPE_NEWDATE:
  241. sqlRowData.data.ulTime = time( NULL ); // FIXME
  242. Assert( !"Not implemented" );
  243. break;
  244. case FIELD_TYPE_ENUM:
  245. case FIELD_TYPE_SET:
  246. case FIELD_TYPE_TINY_BLOB:
  247. case FIELD_TYPE_MEDIUM_BLOB:
  248. case FIELD_TYPE_LONG_BLOB:
  249. case FIELD_TYPE_BLOB:
  250. memset( &sqlRowData.data, 0x0, sizeof( sqlRowData.data ) );
  251. Assert( !"Not implemented" );
  252. break;
  253. case FIELD_TYPE_VAR_STRING:
  254. case FIELD_TYPE_STRING:
  255. {
  256. int cchString = Q_strlen( ( char * )( *pMySQLRow )[iRowData] );
  257. char *pchNewString = new char[ cchString + 1 ];
  258. m_VecPchStoredStrings.AddToTail( pchNewString );
  259. Q_strncpy( pchNewString, ( char * )( *pMySQLRow )[iRowData], cchString + 1 );
  260. pchNewString[ cchString ] = 0;
  261. sqlRowData.data.pchData = pchNewString;
  262. }
  263. break;
  264. }
  265. m_VecSQLRowData.AddToTail( sqlRowData );
  266. }
  267. }
  268. //-----------------------------------------------------------------------------
  269. // Purpose: destructor
  270. //-----------------------------------------------------------------------------
  271. CSQLRow::~CSQLRow()
  272. {
  273. for ( int ipch = 0; ipch < m_VecPchStoredStrings.Count(); ipch++ )
  274. {
  275. delete m_VecPchStoredStrings[ipch];
  276. }
  277. }
  278. //-----------------------------------------------------------------------------
  279. // Purpose: pointer to the char data
  280. //-----------------------------------------------------------------------------
  281. const char *CSQLRow::PchData( int iRowData ) const
  282. {
  283. Assert( m_VecSQLRowData[iRowData].eColumnType == SQL_STRING );
  284. return m_VecSQLRowData[iRowData].data.pchData;
  285. }
  286. //-----------------------------------------------------------------------------
  287. // Purpose: returns the int value of this column
  288. //-----------------------------------------------------------------------------
  289. int CSQLRow::NData( int iRowData ) const
  290. {
  291. Assert( m_VecSQLRowData[iRowData].eColumnType == SQL_INT);
  292. return m_VecSQLRowData[iRowData].data.nData;
  293. }
  294. //-----------------------------------------------------------------------------
  295. // Purpose: returns the uint64 value of this column
  296. //-----------------------------------------------------------------------------
  297. uint64 CSQLRow::UlData( int iRowData ) const
  298. {
  299. Assert( m_VecSQLRowData[iRowData].eColumnType == SQL_UINT64);
  300. return m_VecSQLRowData[iRowData].data.ulData;
  301. }
  302. //-----------------------------------------------------------------------------
  303. // Purpose: returns the float value of this column
  304. //-----------------------------------------------------------------------------
  305. float CSQLRow::FlData( int iRowData ) const
  306. {
  307. Assert( m_VecSQLRowData[iRowData].eColumnType == SQL_FLOAT );
  308. return m_VecSQLRowData[iRowData].data.flData;
  309. }
  310. //-----------------------------------------------------------------------------
  311. // Purpose: returns the time value of this column (time_t equivalent, seconds since midnight (00:00:00), January 1, 1970, coordinated universal time (UTC)
  312. // ignoring daylight savings
  313. //-----------------------------------------------------------------------------
  314. uint64 CSQLRow::UlTime( int iRowData ) const
  315. {
  316. Assert( m_VecSQLRowData[iRowData].eColumnType == SQL_TIME );
  317. return m_VecSQLRowData[iRowData].data.ulTime;
  318. }
  319. //-----------------------------------------------------------------------------
  320. // Purpose: returns the boolean value of this column
  321. //-----------------------------------------------------------------------------
  322. bool CSQLRow::BData( int iRowData ) const
  323. {
  324. Assert( m_VecSQLRowData[iRowData].eColumnType == SQL_INT );
  325. return (m_VecSQLRowData[iRowData].data.nData == 1);
  326. }
  327. //-----------------------------------------------------------------------------
  328. // Purpose: returns the data type contained in this column
  329. //-----------------------------------------------------------------------------
  330. EColumnType CSQLRow::GetEColumnType( int iRowData ) const
  331. {
  332. return m_VecSQLRowData[iRowData].eColumnType;
  333. }
  334. //-----------------------------------------------------------------------------
  335. // Purpose: Ensure that all of our internal structures are consistent, and
  336. // account for all memory that we've allocated.
  337. // Input: validator - Our global validator object
  338. // pchName - Our name (typically a member var in our container)
  339. //-----------------------------------------------------------------------------
  340. #ifdef DBGFLAG_VALIDATE
  341. void CSQLRow::Validate( CValidator &validator, char *pchName )
  342. {
  343. validator.Push( "CSQLRow", this, pchName );
  344. m_VecSQLRowData.Validate( validator, "m_VecSQLRowData" );
  345. m_VecPchStoredStrings.Validate( validator, "m_VecPchStoredStrings" );
  346. for ( int i = 0; i < m_VecPchStoredStrings.Count(); i++ )
  347. {
  348. validator.ClaimMemory( m_VecPchStoredStrings[i] );
  349. }
  350. validator.Pop();
  351. }
  352. #endif
  353. //-----------------------------------------------------------------------------
  354. // Purpose: constructor
  355. //-----------------------------------------------------------------------------
  356. CResultSet::CResultSet()
  357. :m_SQLTableDescription( "Table" )
  358. {
  359. m_pSQLRow = NULL;
  360. m_MySQLRes = NULL;
  361. }
  362. //-----------------------------------------------------------------------------
  363. // Purpose: run a query on the database
  364. //-----------------------------------------------------------------------------
  365. bool CResultSet::Query( const char *pchQuery, ISQLHelper *pSQLHelper )
  366. {
  367. m_SQLTableDescription.Reset();
  368. m_pSQLRow = NULL;
  369. bool bRet = pSQLHelper->BInternalQuery( pchQuery, &m_MySQLRes );
  370. if ( !bRet || !m_MySQLRes )
  371. {
  372. m_cSQLField = -1;
  373. m_cSQLRow = -1;
  374. return false;
  375. }
  376. m_cSQLField = mysql_num_fields( m_MySQLRes );
  377. m_cSQLRow = mysql_num_rows( m_MySQLRes );
  378. MYSQL_FIELD *pMySQLField = mysql_fetch_fields( m_MySQLRes );
  379. for ( int iMySQLField = 0; iMySQLField < m_cSQLField; iMySQLField++ )
  380. {
  381. m_SQLTableDescription.AddColumn( pMySQLField[iMySQLField].name, pMySQLField[iMySQLField].type );
  382. }
  383. return true;
  384. }
  385. //-----------------------------------------------------------------------------
  386. // Purpose: destructor
  387. //-----------------------------------------------------------------------------
  388. CResultSet::~CResultSet()
  389. {
  390. FreeResult();
  391. }
  392. //-----------------------------------------------------------------------------
  393. // Purpose: free any outstanding query
  394. //-----------------------------------------------------------------------------
  395. void CResultSet::FreeResult()
  396. {
  397. if ( m_MySQLRes )
  398. {
  399. mysql_free_result( m_MySQLRes );
  400. m_MySQLRes = NULL;
  401. }
  402. if ( m_pSQLRow )
  403. {
  404. delete m_pSQLRow;
  405. m_pSQLRow = NULL;
  406. }
  407. m_SQLTableDescription.Reset();
  408. }
  409. //-----------------------------------------------------------------------------
  410. // Purpose: number of rows in the result set
  411. //-----------------------------------------------------------------------------
  412. int CResultSet::GetCSQLRow() const
  413. {
  414. return m_cSQLRow;
  415. }
  416. //-----------------------------------------------------------------------------
  417. // Purpose: get the new row from the result set
  418. //-----------------------------------------------------------------------------
  419. const ISQLRow *CResultSet::PSQLRowNextResult()
  420. {
  421. if ( m_pSQLRow )
  422. {
  423. delete m_pSQLRow;
  424. m_pSQLRow = NULL;
  425. }
  426. MYSQL_ROW mySQLRow;
  427. if ( mySQLRow = mysql_fetch_row( m_MySQLRes ) )
  428. {
  429. m_pSQLRow = new CSQLRow( &mySQLRow, &m_SQLTableDescription, m_cSQLField );
  430. }
  431. else
  432. {
  433. FreeResult(); // end of result set
  434. }
  435. return m_pSQLRow;
  436. }
  437. //-----------------------------------------------------------------------------
  438. // Purpose: Ensure that all of our internal structures are consistent, and
  439. // account for all memory that we've allocated.
  440. // Input: validator - Our global validator object
  441. // pchName - Our name (typically a member var in our container)
  442. //-----------------------------------------------------------------------------
  443. #ifdef DBGFLAG_VALIDATE
  444. void CResultSet::Validate( CValidator &validator, char *pchName )
  445. {
  446. validator.Push( "CResultSet", this, pchName );
  447. validator.ClaimMemory( m_MySQLRes );
  448. if ( m_pSQLRow )
  449. {
  450. validator.ClaimMemory( m_pSQLRow );
  451. m_pSQLRow->Validate( validator, "m_pSQLRow" );
  452. }
  453. m_SQLTableDescription.Validate( validator, "m_SQLTableDescription" );
  454. validator.Pop();
  455. }
  456. #endif