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.

1505 lines
49 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================
  7. #include "stdafx.h"
  8. //#include "sqlaccess/sqlaccess.h"
  9. // memdbgon must be the last include file in a .cpp file!!!
  10. #include "tier0/memdbgon.h"
  11. namespace GCSDK
  12. {
  13. #ifndef STEAM
  14. bool isspace( char ch )
  15. {
  16. return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r';
  17. }
  18. int Q_strnlen( const char *str, int count )
  19. {
  20. // can't make more meaningful checks, because this routine is used itself
  21. // to check the NUL-terminatedness of strings
  22. if ( !str || count < 0 )
  23. return -1;
  24. for ( const char *pch = str; pch < str + count; pch++ )
  25. {
  26. if ( *pch == '\0' )
  27. return pch - str;
  28. }
  29. return -1;
  30. }
  31. #endif
  32. //-----------------------------------------------------------------------------
  33. // Purpose: convert an ESchemaCatalog into a string for diagnostics and logging.
  34. // this can't be in enum_names because of data type dependencies.
  35. //-----------------------------------------------------------------------------
  36. const char* PchNameFromESchemaCatalog( ESchemaCatalog e )
  37. {
  38. switch (e)
  39. {
  40. case k_ESchemaCatalogInvalid:
  41. return "k_ESchemaCatalogInvalid";
  42. break;
  43. case k_ESchemaCatalogMain:
  44. return "k_ESchemaCatalogMain";
  45. break;
  46. }
  47. AssertMsg1( false, "unknown ESchemaCatalog (%d)", e );
  48. return "Unknown";
  49. }
  50. //-----------------------------------------------------------------------------
  51. // Purpose: Constructor
  52. //-----------------------------------------------------------------------------
  53. CSchema::CSchema()
  54. {
  55. m_iTable = -1;
  56. m_rgchName[0] = 0;
  57. m_cubRecord = 0;
  58. m_bTestTable = false;
  59. m_cRecordMax = 0;
  60. m_bHasVarFields = false;
  61. m_nHasPrimaryKey = k_EPrimaryKeyTypeNone;
  62. m_iPKIndex = -1;
  63. m_pRecordInfo = NULL;
  64. m_wipePolicy = k_EWipePolicyPreserveAlways;
  65. m_bAllowWipeInProd = false;
  66. m_bPrepopulatedTable = false;
  67. m_nFullTextIndexCatalog = -1;
  68. m_eSchemaCatalog = k_ESchemaCatalogInvalid;
  69. }
  70. //-----------------------------------------------------------------------------
  71. // Purpose: Destructor
  72. //-----------------------------------------------------------------------------
  73. CSchema::~CSchema()
  74. {
  75. SAFE_RELEASE( m_pRecordInfo );
  76. }
  77. //-----------------------------------------------------------------------------
  78. // Purpose: Calculates offset of each field within a record structure, and the
  79. // maximum length of a record structure.
  80. //-----------------------------------------------------------------------------
  81. void CSchema::CalcOffsets()
  82. {
  83. int dubOffsetCur = 0;
  84. for ( int iField = 0; iField < m_VecField.Count(); iField++ )
  85. {
  86. m_VecField[iField].m_dubOffset = dubOffsetCur;
  87. dubOffsetCur += m_VecField[iField].m_cubLength;
  88. if ( m_VecField[iField].BIsVariableLength() )
  89. m_bHasVarFields = true;
  90. }
  91. m_cubRecord = dubOffsetCur;
  92. if ( m_bHasVarFields )
  93. m_cubRecord += sizeof( VarFieldBlockInfo_t );
  94. }
  95. //-----------------------------------------------------------------------------
  96. // Purpose: called to make final calculations when all fields/indexes/etc have
  97. // been added and the schema is ready to be used
  98. //-----------------------------------------------------------------------------
  99. void CSchema::PrepareForUse()
  100. {
  101. // Create a record description (new form of schema information, for SQL) that corresponds to this schema object.
  102. // This contains essentially the information as the CSchema object, we keep both for the moment to bridge the DS and SQL worlds.
  103. Assert( !m_pRecordInfo );
  104. SAFE_RELEASE( m_pRecordInfo );
  105. m_pRecordInfo = CRecordInfo::Alloc();
  106. m_pRecordInfo->InitFromDSSchema( this );
  107. }
  108. //-----------------------------------------------------------------------------
  109. // Purpose: For a record that has variable-length fields, gets the info block from
  110. // the tail end
  111. // Input : pvRecord - Record data
  112. //-----------------------------------------------------------------------------
  113. VarFieldBlockInfo_t* CSchema::PVarFieldBlockInfoFromRecord( const void *pvRecord ) const
  114. {
  115. if ( !m_bHasVarFields )
  116. return NULL;
  117. uint8 *pubRecord = ( uint8* )pvRecord;
  118. return ( VarFieldBlockInfo_t * )( pubRecord + m_cubRecord ) - 1;
  119. }
  120. //-----------------------------------------------------------------------------
  121. // Purpose: Return the total size of the variable-length block for this record
  122. // For records that have no variable-length fields, it will return zero
  123. // Input : pvRecord - Record data
  124. //-----------------------------------------------------------------------------
  125. uint32 CSchema::CubRecordVariable( const void *pvRecord ) const
  126. {
  127. VarFieldBlockInfo_t *pVarFieldsBlockInfo = PVarFieldBlockInfoFromRecord( pvRecord );
  128. if ( pVarFieldsBlockInfo )
  129. return pVarFieldsBlockInfo->m_cubBlock;
  130. else
  131. return 0;
  132. }
  133. void CSchema::RenderField( uint8 *pubRecord, int iField, int cchBuffer, char *pchBuffer )
  134. {
  135. Field_t *pField = &m_VecField[iField];
  136. uint8 *pubData;
  137. uint32 cubData;
  138. char chEmpty = 0;
  139. if ( pField->BIsVariableLength() )
  140. {
  141. if ( !BGetVarField( pubRecord, ( VarField_t * )( pubRecord + pField->m_dubOffset ), &pubData, &cubData ) )
  142. {
  143. // just render a single byte
  144. pubData = ( uint8* )&chEmpty;
  145. cubData = 1;
  146. }
  147. }
  148. else
  149. {
  150. pubData = pubRecord + pField->m_dubOffset;
  151. cubData = m_VecField[iField].m_cubLength;
  152. }
  153. ConvertFieldToText( pField->m_EType, pubData, cubData, pchBuffer, cchBuffer );
  154. }
  155. //-----------------------------------------------------------------------------
  156. // Purpose: Renders a text version of a record to the console.
  157. // Input : pubRecord - Location of the record data
  158. //-----------------------------------------------------------------------------
  159. void CSchema::RenderRecord( uint8 *pubRecord )
  160. {
  161. char rgchT[k_cMedBuff];
  162. // First the header
  163. EmitInfo( SPEW_CONSOLE, 2, 2, "%d\t*** Record header: # of lines ***\n", 1 + m_VecField.Count() );
  164. // Render each field in turn
  165. for ( int iField = 0; iField < m_VecField.Count(); iField++ )
  166. {
  167. Field_t *pField = &m_VecField[iField];
  168. RenderField( pubRecord, iField, Q_ARRAYSIZE(rgchT), rgchT );
  169. EmitInfo( SPEW_CONSOLE, 2, 2, "\t%s\t\t// Field %d (%s)\n", rgchT, iField, pField->m_rgchName );
  170. }
  171. }
  172. //-----------------------------------------------------------------------------
  173. // Purpose: Get data and size of a field, whether fixed or variable length
  174. // Input: pvRecord - Fixed-length part of record
  175. // iField - index of field to get
  176. // ppubField - receives pointer to field data
  177. // pcubField - receives size in bytes of that data
  178. // Output: true if successful
  179. //-----------------------------------------------------------------------------
  180. bool CSchema::BGetFieldData( const void *pvRecord, int iField, uint8 **ppubField, uint32 *pcubField ) const
  181. {
  182. *ppubField = NULL;
  183. *pcubField = 0;
  184. const Field_t &field = m_VecField[iField];
  185. if ( field.BIsVariableLength() )
  186. {
  187. return BGetVarField( pvRecord, ( VarField_t * )( ( uint8 * ) pvRecord + field.m_dubOffset ), ppubField, pcubField );
  188. }
  189. else
  190. {
  191. *ppubField = ( ( uint8 * ) pvRecord + field.m_dubOffset );
  192. *pcubField = field.CubFieldUpdateSize();
  193. }
  194. return true;
  195. }
  196. //-----------------------------------------------------------------------------
  197. // Purpose: Set data and size of a field, whether fixed or variable length
  198. // Input: pvRecord - Fixed-length part of record
  199. // iField - index of field to set
  200. // pubField - pointer to field data to copy from
  201. // cubField - size in bytes of that data
  202. // Output: true if successful
  203. //-----------------------------------------------------------------------------
  204. bool CSchema::BSetFieldData( void *pvRecord, int iField, uint8 *pubField, uint32 cubField, bool *pbVarBlockRealloced )
  205. {
  206. *pbVarBlockRealloced = false;
  207. Field_t &field = m_VecField[iField];
  208. if ( field.BIsVariableLength() )
  209. {
  210. return BSetVarField( pvRecord, ( VarField_t * )( ( uint8 * ) pvRecord + field.m_dubOffset ), pubField, cubField, pbVarBlockRealloced, /*bFreeOnRealloc*/ true );
  211. }
  212. else // fixed length
  213. {
  214. uint8 *pubFieldWrite = ( ( uint8 * ) pvRecord + field.m_dubOffset );
  215. // Must fit in field and last byte for strings MUST be NULL
  216. if ( cubField > field.m_cubLength ||
  217. ( k_EGCSQLType_String == field.m_EType && Q_strnlen( reinterpret_cast< char* >( pubField ), cubField ) == -1 ) )
  218. {
  219. Assert( false );
  220. return false;
  221. }
  222. if ( k_EGCSQLType_Blob != field.m_EType && k_EGCSQLType_Image != field.m_EType )
  223. {
  224. // Copy the data (string or binary, doesn't matter)
  225. if ( cubField > 0 )
  226. Q_memcpy( pubFieldWrite, pubField, cubField );
  227. // Null termination and overwrite any old data
  228. if ( cubField < field.m_cubLength )
  229. Q_memset( pubFieldWrite + cubField, 0, field.m_cubLength - cubField );
  230. }
  231. else
  232. {
  233. // Only support fixed char or binary
  234. Assert( false );
  235. return false;
  236. }
  237. }
  238. return true;
  239. }
  240. //-----------------------------------------------------------------------------
  241. // Purpose: Get data from a variable-length field
  242. // Input: pvRecord - Fixed-length part of record
  243. // pVarField - fixed part of field in that record
  244. // ppubField - receives pointer to field data
  245. // pcubField - receives size in bytes of that data
  246. //-----------------------------------------------------------------------------
  247. bool CSchema::BGetVarField( const void *pvRecord, const VarField_t *pVarField, uint8 **ppubField, uint32 *pcubField ) const
  248. {
  249. Assert( m_bHasVarFields );
  250. *ppubField = 0;
  251. *pcubField = 0;
  252. VarFieldBlockInfo_t *pVarFieldBlockInfo = PVarFieldBlockInfoFromRecord( pvRecord );
  253. if ( !pVarField->m_cubField )
  254. {
  255. *pcubField = 0;
  256. *ppubField = NULL;
  257. return true;
  258. }
  259. if ( pVarFieldBlockInfo->m_cubBlock )
  260. {
  261. uint8 *pubVarBlock = pVarFieldBlockInfo->m_pubBlock;
  262. *ppubField = pubVarBlock + pVarField->m_dubOffset;
  263. *pcubField = pVarField->m_cubField;
  264. // Sanity check
  265. Assert( *pcubField <= k_cubVarFieldMax );
  266. return true;
  267. }
  268. else
  269. {
  270. // Should never happen
  271. Assert( false );
  272. return false;
  273. }
  274. }
  275. //-----------------------------------------------------------------------------
  276. // Purpose: Update a variable-length field in a record (may realloc the var block)
  277. // Input: pvRecord - Fixed-length part of record
  278. // pVarField - fixed part of field in that record
  279. // pvData - data to place in the field
  280. // cubData - size of that data
  281. // pbRealloced - set to indicate if the var block was realloced
  282. // bFreeOnRealloc - If we have to grow or shrink the var block, should we free the old memory?
  283. // Usually true, unless it lives in a NetPacket or something like that
  284. //-----------------------------------------------------------------------------
  285. bool CSchema::BSetVarField( void *pvRecord, VarField_t *pVarField, const void *pvData, uint32 cubData, bool *pbRealloced, bool bFreeOnRealloc )
  286. {
  287. VarFieldBlockInfo_t *pVarFieldBlockInfo = PVarFieldBlockInfoFromRecord( pvRecord );
  288. *pbRealloced = false;
  289. if ( cubData > k_cubVarFieldMax )
  290. {
  291. // field size is too big
  292. Assert( false );
  293. return false;
  294. }
  295. // if no block exists, allocate and copy into it
  296. if ( pVarFieldBlockInfo->m_cubBlock == 0 )
  297. {
  298. // Nothing to do?
  299. if ( !cubData )
  300. return true;
  301. // create it
  302. void *pvBlock = PvAlloc( cubData );
  303. *pbRealloced = true;
  304. // copy the data
  305. Q_memcpy( pvBlock, pvData, cubData );
  306. // set the record's structure to point at our new data
  307. pVarFieldBlockInfo->m_cubBlock = cubData;
  308. pVarFieldBlockInfo->m_pubBlock = ( uint8 * )pvBlock;
  309. // set the field to point at its landing place
  310. pVarField->m_cubField = cubData;
  311. pVarField->m_dubOffset = 0;
  312. }
  313. else
  314. {
  315. // there is some block available.
  316. // is this field changing size?
  317. if ( cubData != 0 && ( cubData == pVarField->m_cubField ) )
  318. {
  319. // no size change - no need to reallocate anything
  320. Q_memcpy( pVarFieldBlockInfo->m_pubBlock + pVarField->m_dubOffset, pvData, cubData );
  321. }
  322. else
  323. {
  324. // size change - realloc
  325. *pbRealloced = true;
  326. uint32 cubFieldOld = pVarField->m_cubField;
  327. uint32 dubOffsetOld = pVarField->m_dubOffset;
  328. uint32 cubBlockNew = pVarFieldBlockInfo->m_cubBlock - pVarField->m_cubField + cubData;
  329. if ( ( cubBlockNew + m_cubRecord ) > k_cubRecordMax )
  330. {
  331. // total record size is too big
  332. Assert( false );
  333. return false;
  334. }
  335. void *pvBlockNew = NULL;
  336. if ( cubBlockNew )
  337. {
  338. // if this field has never been placed in the block (that is, it's not changing an old value)
  339. // and we have enough space, fastest to put it at the end in the free space.
  340. if ( pVarField->m_cubField == 0 && pVarField->m_dubOffset == 0 && cubData <= pVarFieldBlockInfo->m_cubBlockFree )
  341. {
  342. uint8 *pubLastUsed = pVarFieldBlockInfo->m_pubBlock;
  343. for ( int iField = 0; iField < m_VecField.Count(); ++iField )
  344. {
  345. Field_t &field = m_VecField[iField];
  346. if ( field.BIsVariableLength() )
  347. {
  348. // Needs updating
  349. VarField_t *pVarFieldCur = ( VarField_t * )( ( uint8 * )pvRecord + field.m_dubOffset );
  350. pubLastUsed += pVarFieldCur->m_cubField;
  351. }
  352. }
  353. // copy it there
  354. Q_memcpy( pubLastUsed, pvData, cubData );
  355. // set up the field
  356. pVarField->m_cubField = cubData;
  357. pVarField->m_dubOffset = static_cast<int>( (pubLastUsed - pVarFieldBlockInfo->m_pubBlock) );
  358. // note that we used some up
  359. pVarFieldBlockInfo->m_cubBlockFree -= cubData;
  360. }
  361. else
  362. {
  363. // yes ... rellocate
  364. pvBlockNew = PvAlloc( cubBlockNew );
  365. uint8 *pubBlockOldCursor = pVarFieldBlockInfo->m_pubBlock;
  366. uint8 *pubBlockNewCursor = ( uint8* )pvBlockNew;
  367. // copy data, skipping over this field (will put at end)
  368. while ( pubBlockOldCursor < ( pVarFieldBlockInfo->m_pubBlock + pVarFieldBlockInfo->m_cubBlock ) )
  369. {
  370. if ( pVarField->m_cubField && ( (int)pVarField->m_dubOffset == ( pubBlockOldCursor - pVarFieldBlockInfo->m_pubBlock ) ) )
  371. {
  372. pubBlockOldCursor += pVarField->m_cubField;
  373. }
  374. else
  375. {
  376. *pubBlockNewCursor++ = *pubBlockOldCursor++;
  377. }
  378. }
  379. // put this field data at the end
  380. Q_memcpy( pubBlockNewCursor, pvData, cubData );
  381. // free the old block
  382. if ( bFreeOnRealloc )
  383. FreePv( pVarFieldBlockInfo->m_pubBlock );
  384. // Update the block info
  385. pVarFieldBlockInfo->m_cubBlock = cubBlockNew;
  386. pVarFieldBlockInfo->m_pubBlock = ( uint8 * )pvBlockNew;
  387. pVarFieldBlockInfo->m_cubBlockFree = 0;
  388. // update this field
  389. pVarField->m_cubField = cubData;
  390. if ( cubData > 0 )
  391. pVarField->m_dubOffset = static_cast<int>( pubBlockNewCursor - pVarFieldBlockInfo->m_pubBlock );
  392. else
  393. pVarField->m_dubOffset = 0;
  394. // update other fields
  395. for ( int iField = 0; iField < m_VecField.Count(); ++iField )
  396. {
  397. Field_t &field = m_VecField[iField];
  398. if ( field.BIsVariableLength() )
  399. {
  400. // Needs updating
  401. VarField_t *pVarFieldCur = ( VarField_t * )( ( uint8 * )pvRecord + field.m_dubOffset );
  402. // Except the one we just changed
  403. if ( pVarFieldCur == pVarField )
  404. continue;
  405. // And empty fields
  406. if ( !pVarFieldCur->m_cubField )
  407. continue;
  408. if ( pVarFieldCur->m_dubOffset > dubOffsetOld )
  409. pVarFieldCur->m_dubOffset -= cubFieldOld;
  410. }
  411. }
  412. }
  413. }
  414. else
  415. {
  416. // all of the variable data is gone, so
  417. // free the old block
  418. if ( bFreeOnRealloc )
  419. FreePv( pVarFieldBlockInfo->m_pubBlock );
  420. // ... update the block info
  421. pVarFieldBlockInfo->m_cubBlock = 0;
  422. pVarFieldBlockInfo->m_pubBlock = NULL;
  423. pVarFieldBlockInfo->m_cubBlockFree = 0;
  424. // ... and update this field
  425. pVarField->m_dubOffset = 0;
  426. pVarField->m_cubField = cubData;
  427. }
  428. }
  429. }
  430. return true;
  431. }
  432. //-----------------------------------------------------------------------------
  433. // Purpose: If this is a variable-length record, and we just read it from
  434. // a stream, then the var block is after the fixed-length part of the
  435. // record. So, we need to update the record's pointer to reflect that
  436. // Input: pvRecord - Beginning of the serialized record
  437. //-----------------------------------------------------------------------------
  438. void CSchema::FixupDeserializedRecord( void *pvRecord )
  439. {
  440. // Nothing to do if not a variable record
  441. if ( !BHasVariableFields() )
  442. return;
  443. uint8 *pubRecord = ( uint8 * )pvRecord;
  444. VarFieldBlockInfo_t *pVarBlockInfo = PVarFieldBlockInfoFromRecord( pvRecord );
  445. pVarBlockInfo->m_pubBlock = pubRecord + m_cubRecord;
  446. }
  447. //-----------------------------------------------------------------------------
  448. // Purpose: Initializes a new record with random data.
  449. // Input: pubRecord - The record's in-memory data that we'll fill out
  450. // unPrimaryIndex - Primary index of the record
  451. // pbRealloced - Set to 'true' if the var-fields block for this record was reallocated
  452. // (or allocated for the first time)
  453. // bFreeOnRealloc - If true, we should Free() the var block after reallocating a new block
  454. // (should be false if that block lives inside a NetPacket)
  455. //-----------------------------------------------------------------------------
  456. void CSchema::InitRecordRandom( uint8 *pubRecord, uint32 unPrimaryIndex, bool *pbVarBlockRealloced, bool bFreeVarBlockOnRealloc )
  457. {
  458. // Fill out each field in turn
  459. for ( int iField = 0; iField < m_VecField.Count(); iField++ )
  460. {
  461. bool bRealloced = false;
  462. SetFieldRandom( pubRecord, iField, &bRealloced, bFreeVarBlockOnRealloc );
  463. if ( bRealloced )
  464. {
  465. *pbVarBlockRealloced = true;
  466. // if we just allocated it, we can free it next time we need to
  467. bFreeVarBlockOnRealloc = true;
  468. }
  469. }
  470. }
  471. //-----------------------------------------------------------------------------
  472. // Purpose: Sets a single field of a record to a random value.
  473. // Input: pubRecord - Where the record lives in memory
  474. // iField - Which field to set
  475. // pbRealloced - Set to 'true' if the var-fields block for this record was reallocated
  476. // (or allocated for the first time)
  477. // bFreeOnRealloc - If true, we should Free() the var block after reallocating a new block
  478. // (should be false if that block lives inside a NetPacket)
  479. //-----------------------------------------------------------------------------
  480. void CSchema::SetFieldRandom( uint8 *pubRecord, int iField, bool *pbVarBlockRealloced, bool bFreeVarBlockOnRealloc )
  481. {
  482. Field_t &field = m_VecField[iField];
  483. *pbVarBlockRealloced = false;
  484. // Strings get random text
  485. if ( k_EGCSQLType_String == field.m_EType )
  486. {
  487. // Generate up to cubLength - 1 chars
  488. uint32 cch = UNRandFast() % field.m_cubLength;
  489. uint32 ich = 0;
  490. for ( ; ich < cch; ich++ )
  491. *( ( char * ) pubRecord + field.m_dubOffset + ich ) = CHRandFast();
  492. // Null termination
  493. for ( ; ich < field.m_cubLength; ich++ )
  494. *( ( char * ) pubRecord + field.m_dubOffset + ich ) = 0;
  495. }
  496. else if ( field.BIsVariableLength() )
  497. {
  498. // Need temp buffer to randomize before setting
  499. // kept reasonably small to prevent spamming the console
  500. uint8 rgubBuff[512];
  501. uint32 cubData = UNRandFast() % sizeof(rgubBuff);
  502. // For strings, put in (cubData-1) random characters (each randomly in the range [32,126])
  503. // then a trailing NULL
  504. if ( field.BIsStringType() )
  505. {
  506. uint32 ich = 0;
  507. for ( ; ich < (cubData-1); ich++ )
  508. rgubBuff[ich] = CHRandFast();
  509. rgubBuff[ich] = 0;
  510. }
  511. else
  512. {
  513. // binary - just fill in random bytes
  514. for ( uint32 iub = 0; iub < cubData; iub++ )
  515. {
  516. rgubBuff[iub] = ( uint8 )( UNRandFast() % 256 );
  517. }
  518. }
  519. // Set the variable field in the record
  520. BSetVarField( pubRecord, ( VarField_t * ) ( pubRecord + field.m_dubOffset ), rgubBuff, cubData, pbVarBlockRealloced, bFreeVarBlockOnRealloc );
  521. }
  522. // Binaries are filled with completely random bytes
  523. else
  524. {
  525. for ( uint32 iub = 0; iub < field.m_cubLength; iub++ )
  526. {
  527. *( pubRecord + field.m_dubOffset + iub ) = ( uint8 ) ( UNRandFast() % 256 );
  528. }
  529. }
  530. }
  531. //-----------------------------------------------------------------------------
  532. // Purpose: Returns checksum of our contents
  533. // Output : checksum
  534. //-----------------------------------------------------------------------------
  535. uint32 CSchema::CalcChecksum()
  536. {
  537. CRC32_t crc32;
  538. CRC32_Init( &crc32 );
  539. FOR_EACH_VEC( m_VecField, nField )
  540. {
  541. CRC32_ProcessBuffer( &crc32, &m_VecField[nField], sizeof( m_VecField[nField] ) );
  542. }
  543. // keep checksum for entire record info
  544. CRC32_Final( &crc32 );
  545. return (uint32)crc32;
  546. }
  547. void CSchema::AddIntField( char *pchName, char *pchSQLName, EGCSQLType eType, int cubSize )
  548. {
  549. int nExpectedSize = -1;
  550. switch ( eType )
  551. {
  552. case k_EGCSQLType_int8:
  553. nExpectedSize = 1;
  554. break;
  555. case k_EGCSQLType_int16:
  556. nExpectedSize = 2;
  557. break;
  558. case k_EGCSQLType_int32:
  559. case k_EGCSQLType_float:
  560. nExpectedSize = 4;
  561. break;
  562. case k_EGCSQLType_int64:
  563. case k_EGCSQLType_double:
  564. nExpectedSize = 8;
  565. break;
  566. }
  567. AssertMsg2( nExpectedSize != -1, "Unexpected EType in AddIntField: %d for column %s", eType, pchSQLName );
  568. AssertMsg3( nExpectedSize == cubSize, "Unexpected size for in AddIntField for column %s: %d doesn't match %d ", pchSQLName, nExpectedSize, cubSize );
  569. AddField( pchName, pchSQLName, eType, cubSize, 0 );
  570. }
  571. //-----------------------------------------------------------------------------
  572. // Purpose: Adds a field from our intrinsic schema to this schema.
  573. // Input: pchName - Name of the field
  574. // pchSQLName - Name of the field in SQL database
  575. // eType - Type of the field
  576. // cubSize - Size of the field
  577. // pfnCompare - Function used to compare fields (NULL if none specified)
  578. //-----------------------------------------------------------------------------
  579. void CSchema::AddField( char *pchName, char *pchSQLName, EGCSQLType eType, uint32 cubSize, int cchMaxLength )
  580. {
  581. int iFieldNew = m_VecField.AddToTail();
  582. Field_t &field = m_VecField[iFieldNew];
  583. field.m_EType = eType;
  584. field.m_cubLength = cubSize;
  585. field.m_nColFlags = 0;
  586. Q_strncpy( field.m_rgchName, pchName, Q_ARRAYSIZE( field.m_rgchName ) );
  587. Q_strncpy( field.m_rgchSQLName, pchSQLName, Q_ARRAYSIZE( field.m_rgchSQLName ) );
  588. field.m_cchMaxLength = cchMaxLength;
  589. }
  590. //-----------------------------------------------------------------------------
  591. // Purpose: Marks a given field as the primary index
  592. // Input:
  593. // bClustered - true to create a clustered index
  594. // pchName - Name of the field to make primary index
  595. //-----------------------------------------------------------------------------
  596. int CSchema::PrimaryKey( bool bClustered, int nFillFactor, const char *pchName )
  597. {
  598. int iField = FindIField( pchName );
  599. AssertFatalMsg2( k_iFieldNil != iField, "Could not find index column \"%s\" on table \"%s\"", pchName, m_rgchName );
  600. Assert( m_nHasPrimaryKey == k_EPrimaryKeyTypeNone ); // may not already have a primary key defined
  601. Assert( m_iPKIndex == k_iFieldNil );
  602. // must not have been indexed in any way before
  603. Assert( 0 == ( m_VecField[ iField ].m_nColFlags & ( k_nColFlagPrimaryKey | k_nColFlagIndexed | k_nColFlagUnique | k_nColFlagClustered ) ) );
  604. // note that we have a single-column PK
  605. m_nHasPrimaryKey = k_EPrimaryKeyTypeSingle;
  606. // set our flags
  607. m_VecField[ iField ].m_nColFlags |= ( k_nColFlagPrimaryKey | k_nColFlagIndexed | k_nColFlagUnique );
  608. if ( bClustered )
  609. m_VecField[ iField ].m_nColFlags |= k_nColFlagClustered;
  610. // add to list of primary keys and remember the indexID
  611. CUtlVector<int> vecColumns;
  612. vecColumns.AddToTail( iField );
  613. FieldSet_t vecIndex( true /* unique */ , bClustered, vecColumns, NULL );
  614. vecIndex.SetFillFactor( nFillFactor );
  615. m_iPKIndex = m_VecIndexes.AddToTail( vecIndex );
  616. return m_iPKIndex;
  617. }
  618. //-----------------------------------------------------------------------------
  619. // Purpose: Marks a set of fields as the primary index
  620. // Input:
  621. // bClustered - clustered index created if true
  622. // nFillFactor - fill facto to use; 0 is database default
  623. // pchName - Name of the field to make primary index
  624. //-----------------------------------------------------------------------------
  625. int CSchema::PrimaryKeys( bool bClustered, int nFillFactor, const char *pchNames )
  626. {
  627. Assert( pchNames != NULL ); // no bogus parameters, please
  628. Assert( m_nHasPrimaryKey == k_EPrimaryKeyTypeNone ); // may not already have a primary key defined
  629. Assert( m_iPKIndex == k_iFieldNil ); // no primary key defined
  630. int nFlags = k_nColFlagPrimaryKey | k_nColFlagIndexed | k_nColFlagUnique;
  631. if ( bClustered )
  632. nFlags |= k_nColFlagClustered;
  633. // go add all those fields as Indexed
  634. int nNewIndex = AddIndexToFieldList( pchNames, NULL, nFlags, nFillFactor );
  635. if ( nNewIndex != k_iFieldNil )
  636. {
  637. m_nHasPrimaryKey = k_EPrimaryKeyTypeMulti; // remember that we have multiple keys
  638. m_iPKIndex = nNewIndex;
  639. }
  640. return nNewIndex;
  641. }
  642. //-----------------------------------------------------------------------------
  643. // Purpose: Marks a set of fields as the primary index
  644. // Input:
  645. // pchName - Names of the fields for the primary index; comma-separated if multiple
  646. // pchIndexName - Name of the index object (not in SQL)
  647. // nFlags - flags for CColumnInfo on this object
  648. // nFillFactor - fill facto to use; 0 is database default
  649. //-----------------------------------------------------------------------------
  650. int CSchema::AddIndexToFieldList( const char *pchNames, const char *pchIndexName, int nFlags, int nFillFactor )
  651. {
  652. // need a copy of string list since the argument is const and read-only
  653. int cNamesLen = Q_strlen( pchNames ) + 1;
  654. char* pchNamesCopy = (char*) PvAlloc( cNamesLen );
  655. Q_strncpy( pchNamesCopy, pchNames, cNamesLen );
  656. if (pchNames == NULL)
  657. {
  658. // not enough memory!
  659. AssertFatal( false );
  660. return k_iFieldNil;
  661. }
  662. CUtlVector<int> vecKeys;
  663. char* pchCurrent = pchNamesCopy;
  664. do
  665. {
  666. // find next token
  667. char* pchEnd = strchr(pchCurrent, ',');
  668. if ( pchEnd != NULL )
  669. {
  670. *pchEnd++ = 0;
  671. }
  672. // skip whitespace
  673. while (isspace(*pchCurrent))
  674. pchCurrent++;
  675. // find the name; we expect a C++ name, not a SQL name
  676. int iField = FindIField( pchCurrent );
  677. AssertFatalMsg2( k_iFieldNil != iField, "Could not find index column \"%s\" on table \"%s\"", pchCurrent, m_rgchName );
  678. // mark as a primary key
  679. m_VecField[iField].m_nColFlags |= nFlags ;
  680. // add it to our collection
  681. vecKeys.AddToTail( iField );
  682. // move past the end of this token in the string
  683. pchCurrent = pchEnd;
  684. } while ( pchCurrent != NULL );
  685. // release our copy
  686. FreePv(pchNamesCopy);
  687. // create a fieldset with our list of indexes
  688. // and add it to our indexes collection
  689. bool bUnique = 0 != (nFlags & k_nColFlagUnique);
  690. bool bClustered = 0 != (nFlags & k_nColFlagClustered);
  691. FieldSet_t vecIndex( bUnique, bClustered, vecKeys, pchIndexName );
  692. vecIndex.SetFillFactor( nFillFactor );
  693. int iReturn = m_VecIndexes.AddToTail( vecIndex );
  694. return iReturn;
  695. }
  696. //-----------------------------------------------------------------------------
  697. // Purpose: Marks a given field as indexed.
  698. // Input: pchName - Name of the field to index
  699. // pchIndexName - Name of the index object (not in SQL)
  700. //-----------------------------------------------------------------------------
  701. int CSchema::IndexField( const char *pchIndexName, const char *pchName )
  702. {
  703. Assert( pchName != NULL );
  704. Assert( pchIndexName != NULL );
  705. // find the field by name
  706. int iField = FindIField( pchName );
  707. AssertFatalMsg2( k_iFieldNil != iField, "Could not find index column \"%s\" on table \"%s\"", pchName, m_rgchName );
  708. // add an index to it
  709. return AddIndexToFieldNumber( iField, pchIndexName, false /* Not clustered */ );
  710. }
  711. //-----------------------------------------------------------------------------
  712. // Purpose: Marks a given field as indexed.
  713. // Input: pchIndexName - Name of the index object (not in SQL)
  714. // pchName - Name of the fields to index; comma separated if multiple
  715. //-----------------------------------------------------------------------------
  716. int CSchema::IndexFields( const char *pchIndexName, const char *pchNames )
  717. {
  718. Assert( pchNames != NULL );
  719. Assert( pchIndexName != NULL );
  720. // go add all those fields as Indexed
  721. int nNewIndex = AddIndexToFieldList( pchNames, pchIndexName, k_nColFlagIndexed, 0 );
  722. return nNewIndex;
  723. }
  724. //-----------------------------------------------------------------------------
  725. // Purpose: Includes a column (or columns) in an index for the INCLUDE clause
  726. // Input: pchIndexName - Name of the index object (not in SQL)
  727. // pchName - Name of the fields to index; comma separated if multiple
  728. //-----------------------------------------------------------------------------
  729. void CSchema::AddIncludedFields( const char *pchIndexName, const char *pchNames )
  730. {
  731. Assert( pchNames != NULL );
  732. Assert( pchIndexName != NULL );
  733. // find that index by name
  734. int nIndexIndex = -1;
  735. FOR_EACH_VEC( m_VecIndexes, i )
  736. {
  737. FieldSet_t &refSet = m_VecIndexes.Element(i);
  738. const char *pstrMatch = refSet.GetIndexName();
  739. if ( Q_stricmp( pstrMatch, pchIndexName ) == 0 )
  740. {
  741. nIndexIndex = i;
  742. break;
  743. }
  744. }
  745. // must have found it
  746. AssertFatalMsg1( nIndexIndex != -1, "Index \"%s\" not found", pchIndexName );
  747. FieldSet_t &refSet = m_VecIndexes.Element( nIndexIndex );
  748. // need a copy of string list since the argument is const and read-only
  749. int cNamesLen = Q_strlen( pchNames ) + 1;
  750. char* pchNamesCopy = (char*) PvAlloc( cNamesLen );
  751. Q_strncpy( pchNamesCopy, pchNames, cNamesLen );
  752. char* pchCurrent = pchNamesCopy;
  753. do
  754. {
  755. // find next token
  756. char* pchEnd = strchr(pchCurrent, ',');
  757. if ( pchEnd != NULL )
  758. {
  759. *pchEnd++ = 0;
  760. }
  761. // skip whitespace
  762. while (isspace(*pchCurrent))
  763. pchCurrent++;
  764. // find the name; we expect a C++ name, not a SQL name
  765. int iField = FindIField( pchCurrent );
  766. AssertFatalMsg2( k_iFieldNil != iField, "Could not find index column \"%s\" on table \"%s\"", pchCurrent, m_rgchName );
  767. // add it to our collection
  768. refSet.AddIncludedColumn( iField );
  769. // move past the end of this token in the string
  770. pchCurrent = pchEnd;
  771. } while ( pchCurrent != NULL );
  772. // release our copy
  773. FreePv( pchNamesCopy );
  774. return;
  775. }
  776. //-----------------------------------------------------------------------------
  777. // Purpose: Marks a given field as indexed.
  778. // Input: pchIndexName - Name of the index object (not in SQL)
  779. // pchName - Name of the fields to index; comma separated if multiple
  780. //-----------------------------------------------------------------------------
  781. int CSchema::UniqueFields( const char *pchIndexName, const char *pchNames )
  782. {
  783. Assert( pchNames != NULL );
  784. Assert( pchIndexName != NULL );
  785. // go add all those fields as Indexed
  786. int nNewIndex = AddIndexToFieldList( pchNames, pchIndexName, k_nColFlagIndexed | k_nColFlagUnique, 0 );
  787. return nNewIndex;
  788. }
  789. //-----------------------------------------------------------------------------
  790. // Purpose: Marks a given field a having a clustered index.
  791. // Input: pchName - Name of the field to index
  792. // pchIndexName - Name of the index object (not in SQL)
  793. //-----------------------------------------------------------------------------
  794. int CSchema::ClusteredIndexField( int nFillFactor, const char *pchIndexName, const char *pchName )
  795. {
  796. Assert( pchName != NULL );
  797. Assert( pchIndexName != NULL );
  798. // can't previously have some other clustered index
  799. FOR_EACH_VEC( m_VecIndexes, iIndex )
  800. {
  801. if ( m_VecIndexes[iIndex].IsClustered() )
  802. {
  803. AssertFatalMsg3( false, "On table %s, can't make index %s clustered because %s is already the clustered index\n",
  804. m_rgchName, pchIndexName, m_VecIndexes[iIndex].GetIndexName() );
  805. return -1;
  806. }
  807. }
  808. // find the field by name
  809. int iField = FindIField( pchName );
  810. AssertFatalMsg2( k_iFieldNil != iField, "Could not find index column \"%s\" on table \"%s\"", pchName, m_rgchName );
  811. // add an index to it
  812. int iIndex = AddIndexToFieldNumber( iField, pchIndexName, true /* clustered */ );
  813. m_VecIndexes[iIndex].SetFillFactor( nFillFactor );
  814. return iIndex;
  815. }
  816. //-----------------------------------------------------------------------------
  817. // Purpose: Marks a given set of fields as having a clustered index.
  818. // Input: pchIndexName - Name of the index object (not in SQL)
  819. // pchName - Name of the fields to index; comma separated if multiple
  820. //-----------------------------------------------------------------------------
  821. int CSchema::ClusteredIndexFields( int nFillFactor, const char *pchIndexName, const char *pchNames )
  822. {
  823. Assert( pchNames != NULL );
  824. Assert( pchIndexName != NULL );
  825. // can't previously have some other clustered index
  826. FOR_EACH_VEC( m_VecIndexes, iIndex )
  827. {
  828. if ( m_VecIndexes[iIndex].IsClustered() )
  829. {
  830. AssertFatalMsg3( false, "On table %s, can't make index %s clustered because %s is already the clustered index\n",
  831. m_rgchName, pchIndexName, m_VecIndexes[iIndex].GetIndexName() );
  832. return -1;
  833. }
  834. }
  835. // go add all those fields as Indexed
  836. int nNewIndex = AddIndexToFieldList( pchNames, pchIndexName, k_nColFlagClustered | k_nColFlagIndexed, nFillFactor );
  837. return nNewIndex;
  838. }
  839. //-----------------------------------------------------------------------------
  840. //-----------------------------------------------------------------------------
  841. void CSchema::AddFullTextIndex( CSchemaFull *pSchemaFull, const char *pchCatalogName, const char *pchColumnName )
  842. {
  843. Assert( pchCatalogName != NULL );
  844. Assert( pchColumnName != NULL );
  845. // need a copy of string list since the argument is const and read-only
  846. int cNamesLen = Q_strlen( pchColumnName ) + 1;
  847. char* pchNamesCopy = (char*) PvAlloc( cNamesLen );
  848. Q_strncpy( pchNamesCopy, pchColumnName, cNamesLen );
  849. if ( pchNamesCopy == NULL )
  850. {
  851. // not enough memory!
  852. AssertFatal( false );
  853. return;
  854. }
  855. char* pchCurrent = pchNamesCopy;
  856. do
  857. {
  858. // find next token
  859. char* pchEnd = strchr(pchCurrent, ',');
  860. if ( pchEnd != NULL )
  861. {
  862. *pchEnd++ = 0;
  863. }
  864. // skip whitespace
  865. while (isspace(*pchCurrent))
  866. pchCurrent++;
  867. // find the name; we expect a C++ name, not a SQL name
  868. int iField = FindIField( pchCurrent );
  869. AssertFatalMsg2( k_iFieldNil != iField, "Could not find index column \"%s\" on table \"%s\"", pchCurrent, m_rgchName );
  870. // add it to our collection
  871. m_VecFullTextIndexes.AddToTail( iField );
  872. // move past the end of this token in the string
  873. pchCurrent = pchEnd;
  874. } while ( pchCurrent != NULL );
  875. // release our copy
  876. FreePv(pchNamesCopy);
  877. // make a note of the catalog we want
  878. int nCatalogID = pSchemaFull->GetFTSCatalogByName( m_eSchemaCatalog, pchCatalogName );
  879. AssertFatalMsg2( nCatalogID != -1, "Could not find fulltext catalog \"%s\" on table \"%s\"", pchCatalogName, m_rgchName );
  880. m_nFullTextIndexCatalog = nCatalogID;
  881. return;
  882. }
  883. //-----------------------------------------------------------------------------
  884. // Purpose: Marks a field as indexed given its field number
  885. //-----------------------------------------------------------------------------
  886. int CSchema::AddIndexToFieldNumber( int iField, const char *pchIndexName, bool bClustered )
  887. {
  888. // mark the field as indexed
  889. m_VecField[iField].m_nColFlags |= k_nColFlagIndexed;
  890. // meant to be clustered?
  891. if ( bClustered )
  892. m_VecField[iField].m_nColFlags |= k_nColFlagClustered;
  893. // add to list of indexes, and remember the indexID
  894. CUtlVector<int> vecColumns;
  895. vecColumns.AddToTail( iField );
  896. // false: not unique
  897. // false: not clustered
  898. FieldSet_t vecIndex( false, bClustered, vecColumns, pchIndexName);
  899. int iIndex = m_VecIndexes.AddToTail( vecIndex );
  900. return iIndex;
  901. }
  902. //-----------------------------------------------------------------------------
  903. // Purpose: Marks a given field as unique.
  904. // Input: pchName - Name of the field to index
  905. // pchIndexName - Name of the index object
  906. //-----------------------------------------------------------------------------
  907. int CSchema::UniqueField( const char *pchIndexName, const char *pchName )
  908. {
  909. Assert( pchName != NULL );
  910. Assert( pchIndexName != NULL );
  911. int iField = FindIField( pchName );
  912. AssertFatalMsg2( k_iFieldNil != iField, "Could not find index column \"%s\" on table \"%s\"", pchName, m_rgchName );
  913. m_VecField[iField].m_nColFlags |= ( k_nColFlagUnique | k_nColFlagIndexed );
  914. // add to list of indexes, and remember the indexID
  915. CUtlVector<int> vecColumns;
  916. vecColumns.AddToTail( iField );
  917. // true: unique
  918. // false: not clustered
  919. FieldSet_t vecIndex( true, false, vecColumns, pchIndexName );
  920. int iIndex = m_VecIndexes.AddToTail( vecIndex );
  921. return iIndex;
  922. }
  923. //-----------------------------------------------------------------------------
  924. // Purpose: Marks a given field as auto increment.
  925. // Input: pchName - Name of the field
  926. //-----------------------------------------------------------------------------
  927. void CSchema::AutoIncrementField( char *pchName )
  928. {
  929. int iField = FindIField( pchName );
  930. AssertFatalMsg2( k_iFieldNil != iField, "Could not find index column \"%s\" on table \"%s\"", pchName, m_rgchName );
  931. m_VecField[iField].m_nColFlags |= k_nColFlagAutoIncrement;
  932. }
  933. //-----------------------------------------------------------------------------
  934. // Purpose: Finds the field with a given name.
  935. // Input: pchName - Name of the field to search for
  936. // Output: Index of the matching field (k_iFieldNil if there isn't one)
  937. //-----------------------------------------------------------------------------
  938. int CSchema::FindIField( const char *pchName )
  939. {
  940. for ( int iField = 0; iField < m_VecField.Count(); iField++ )
  941. {
  942. if ( 0 == Q_strncmp( pchName, m_VecField[iField].m_rgchName, k_cSQLObjectNameMax ) )
  943. return iField;
  944. }
  945. return k_iFieldNil;
  946. }
  947. //-----------------------------------------------------------------------------
  948. // Purpose: Finds the field with a given SQL name.
  949. // Input: pchName - Name of the field to search for
  950. // Output: Index of the matching field (k_iFieldNil if there isn't one)
  951. //-----------------------------------------------------------------------------
  952. int CSchema::FindIFieldSQL( const char *pchName )
  953. {
  954. for ( int iField = 0; iField < m_VecField.Count(); iField++ )
  955. {
  956. if ( 0 == Q_strncmp( pchName, m_VecField[iField].m_rgchSQLName, k_cSQLObjectNameMax ) )
  957. return iField;
  958. }
  959. return k_iFieldNil;
  960. }
  961. //-----------------------------------------------------------------------------
  962. // Purpose: Adds a schema conversion instruction (for use in converting from
  963. // a different Schema to this one).
  964. //-----------------------------------------------------------------------------
  965. void CSchema::AddDeleteField( const char *pchFieldName )
  966. {
  967. DeleteField_t &deleteField = m_VecDeleteField[m_VecDeleteField.AddToTail()];
  968. Q_strncpy( deleteField.m_rgchFieldName, pchFieldName, sizeof( deleteField.m_rgchFieldName ) );
  969. }
  970. //-----------------------------------------------------------------------------
  971. // Purpose: Adds a schema conversion instruction (for use in converting from
  972. // a different Schema to this one).
  973. //-----------------------------------------------------------------------------
  974. void CSchema::AddRenameField( const char *pchFieldNameOld, const char *pchFieldNameNew )
  975. {
  976. RenameField_t &renameField = m_VecRenameField[m_VecRenameField.AddToTail()];
  977. Q_strncpy( renameField.m_rgchFieldNameOld, pchFieldNameOld, sizeof( renameField.m_rgchFieldNameOld ) );
  978. renameField.m_iFieldDst = FindIField( pchFieldNameNew );
  979. Assert( k_iFieldNil != renameField.m_iFieldDst );
  980. }
  981. //-----------------------------------------------------------------------------
  982. // Purpose: Adds a schema conversion instruction (for use in converting from
  983. // a different Schema to this one).
  984. // Input: pchFieldNameOld - Name of the field in the old schema
  985. // pchFieldNameMew - Name of the field in the new schema
  986. // pfnAlterField - Function to translate data from the old format to
  987. // the new
  988. //-----------------------------------------------------------------------------
  989. void CSchema::AddAlterField( const char *pchFieldNameOld, const char *pchFieldNameNew, PfnAlterField_t pfnAlterField )
  990. {
  991. Assert( pfnAlterField );
  992. AlterField_t &alterField = m_VecAlterField[m_VecAlterField.AddToTail()];
  993. Q_strncpy( alterField.m_rgchFieldNameOld, pchFieldNameOld, sizeof( alterField.m_rgchFieldNameOld ) );
  994. alterField.m_iFieldDst = FindIField( pchFieldNameNew );
  995. Assert( k_iFieldNil != alterField.m_iFieldDst );
  996. alterField.m_pfnAlterFunc = pfnAlterField;
  997. }
  998. //-----------------------------------------------------------------------------
  999. // Purpose: Figures out how to map a field from another Schema into us.
  1000. // First we check our conversion instructions to see if any apply,
  1001. // and then we look for a straightforward match.
  1002. // Input: pchFieldName - Name of the field we're trying to map
  1003. // piFieldDst - [Return] Index of the field to map it to
  1004. // ppfnAlterField - [Return] Optional function to convert data
  1005. // Output: true if we know what to do with this field (if false, the conversion
  1006. // is undefined and dangerous).
  1007. //-----------------------------------------------------------------------------
  1008. bool CSchema::BCanConvertField( const char *pchFieldName, int *piFieldDst, PfnAlterField_t *ppfnAlterField )
  1009. {
  1010. *ppfnAlterField = NULL;
  1011. // Should this field be deleted?
  1012. for ( int iDeleteField = 0; iDeleteField < m_VecDeleteField.Count(); iDeleteField++ )
  1013. {
  1014. if ( 0 == Q_strcmp( pchFieldName, m_VecDeleteField[iDeleteField].m_rgchFieldName ) )
  1015. {
  1016. *piFieldDst = k_iFieldNil;
  1017. return true;
  1018. }
  1019. }
  1020. // Should this field be renamed?
  1021. for ( int iRenameField = 0; iRenameField < m_VecRenameField.Count(); iRenameField++ )
  1022. {
  1023. if ( 0 == Q_strcmp( pchFieldName, m_VecRenameField[iRenameField].m_rgchFieldNameOld ) )
  1024. {
  1025. *piFieldDst = m_VecRenameField[iRenameField].m_iFieldDst;
  1026. return true;
  1027. }
  1028. }
  1029. // Was this field altered?
  1030. for ( int iAlterField = 0; iAlterField < m_VecAlterField.Count(); iAlterField++ )
  1031. {
  1032. if ( 0 == Q_strcmp( pchFieldName, m_VecAlterField[iAlterField].m_rgchFieldNameOld ) )
  1033. {
  1034. *piFieldDst = m_VecAlterField[iAlterField].m_iFieldDst;
  1035. *ppfnAlterField = m_VecAlterField[iAlterField].m_pfnAlterFunc;
  1036. return true;
  1037. }
  1038. }
  1039. // Find out which of our fields this field maps to (if it doesn't map
  1040. // to any of them, we don't know what to do with it).
  1041. *piFieldDst = FindIField( pchFieldName );
  1042. return ( k_iFieldNil != *piFieldDst );
  1043. }
  1044. //-----------------------------------------------------------------------------
  1045. // Purpose: Return the size to use when writing to a field.
  1046. //-----------------------------------------------------------------------------
  1047. int Field_t::CubFieldUpdateSize() const
  1048. {
  1049. switch ( m_EType )
  1050. {
  1051. case k_EGCSQLType_String:
  1052. case k_EGCSQLType_Blob:
  1053. case k_EGCSQLType_Image:
  1054. // Nobody should call this function for
  1055. // var-length fields
  1056. Assert( false );
  1057. return m_cubLength;
  1058. case k_EGCSQLType_int8:
  1059. case k_EGCSQLType_int16:
  1060. case k_EGCSQLType_int32:
  1061. case k_EGCSQLType_int64:
  1062. case k_EGCSQLType_float:
  1063. case k_EGCSQLType_double:
  1064. return m_cubLength;
  1065. default:
  1066. Assert(false);
  1067. return 0;
  1068. }
  1069. }
  1070. //-----------------------------------------------------------------------------
  1071. // Purpose: Tell whether or not a field is of string type.
  1072. //-----------------------------------------------------------------------------
  1073. bool Field_t::BIsStringType() const
  1074. {
  1075. return ( k_EGCSQLType_String == m_EType );
  1076. }
  1077. //-----------------------------------------------------------------------------
  1078. // Purpose: Tell whether or not the field is of variable-length type.
  1079. //-----------------------------------------------------------------------------
  1080. bool Field_t::BIsVariableLength() const
  1081. {
  1082. return ( k_EGCSQLType_String == m_EType ) || ( k_EGCSQLType_Blob == m_EType ) || ( k_EGCSQLType_Image == m_EType );
  1083. }
  1084. //-----------------------------------------------------------------------------
  1085. // Purpose: Add a foreign key
  1086. //-----------------------------------------------------------------------------
  1087. void CSchema::AddFK( const char* pchName, const char* pchColumn, const char* pchParentTable, const char* pchParentColumn, EForeignKeyAction eOnDeleteAction, EForeignKeyAction eOnUpdateAction )
  1088. {
  1089. int iTail = m_VecFKData.AddToTail();
  1090. FKData_t &fkData = m_VecFKData[iTail];
  1091. Q_snprintf( fkData.m_rgchName, Q_ARRAYSIZE( fkData.m_rgchName ), "%s_%s", GetPchName(), pchName );
  1092. Q_strncpy( fkData.m_rgchParentTableName, pchParentTable, Q_ARRAYSIZE( fkData.m_rgchParentTableName ) );
  1093. fkData.m_eOnDeleteAction = eOnDeleteAction;
  1094. fkData.m_eOnUpdateAction = eOnUpdateAction;
  1095. // Now we need to split up the column name strings and add their data
  1096. FKColumnRelation_t colRelation;
  1097. Q_memset( &colRelation, 0, sizeof( FKColumnRelation_t ) );
  1098. uint iMyColumn = 0;
  1099. uint iParentColumn = 0;
  1100. uint iParentString = 0;
  1101. for( uint i=0; i<(uint)Q_strlen( pchColumn )+1; ++i )
  1102. {
  1103. if ( pchColumn[i] != ',' && pchColumn[i] != 0 )
  1104. {
  1105. colRelation.m_rgchCol[ iMyColumn++ ] = pchColumn[i];
  1106. }
  1107. else
  1108. {
  1109. Assert( Q_strlen( colRelation.m_rgchCol ) );
  1110. // Should have a matching column name in the parent string
  1111. while( iParentString < (uint)Q_strlen( pchParentColumn ) )
  1112. {
  1113. if ( pchParentColumn[iParentString] != ',' )
  1114. {
  1115. colRelation.m_rgchParentCol[ iParentColumn++ ] = pchParentColumn[iParentString];
  1116. ++iParentString;
  1117. }
  1118. else
  1119. {
  1120. ++iParentString;
  1121. break;
  1122. }
  1123. }
  1124. AssertMsg( Q_strlen( colRelation.m_rgchParentCol ), "Column counts for FK do not match between child/parent\n" );
  1125. fkData.m_VecColumnRelations.AddToTail( colRelation );
  1126. Q_memset( &colRelation, 0, sizeof( FKColumnRelation_t ) );
  1127. // Reset positions
  1128. iMyColumn = 0;
  1129. iParentColumn = 0;
  1130. }
  1131. }
  1132. }
  1133. //-----------------------------------------------------------------------------
  1134. // Purpose: Caches the insert statement for each table so we don't need to
  1135. // generate it for each row we insert.
  1136. //-----------------------------------------------------------------------------
  1137. const char *CSchema::GetInsertStatementText() const
  1138. {
  1139. if ( m_sInsertStatementText.IsEmpty() )
  1140. {
  1141. TSQLCmdStr sBuilder;
  1142. BuildInsertStatementText( &sBuilder, GetRecordInfo() );
  1143. m_sInsertStatementText.Set( sBuilder );
  1144. }
  1145. return m_sInsertStatementText;
  1146. }
  1147. //-----------------------------------------------------------------------------
  1148. // Purpose: Caches the insert via MERGE statement for each table so we don't need to
  1149. // generate it for each row we insert.
  1150. //-----------------------------------------------------------------------------
  1151. const char *CSchema::GetMergeStatementTextOnPKWhenMatchedUpdateWhenNotMatchedInsert()
  1152. {
  1153. if ( m_sMergeStatementTextOnPKWhenMatchedUpdateWhenNotMatchedInsert.IsEmpty() )
  1154. {
  1155. TSQLCmdStr sBuilder;
  1156. BuildMergeStatementTextOnPKWhenMatchedUpdateWhenNotMatchedInsert( &sBuilder, GetRecordInfo() );
  1157. m_sMergeStatementTextOnPKWhenMatchedUpdateWhenNotMatchedInsert.Set( sBuilder );
  1158. }
  1159. return m_sMergeStatementTextOnPKWhenMatchedUpdateWhenNotMatchedInsert;
  1160. }
  1161. //-----------------------------------------------------------------------------
  1162. // Purpose: Caches the insert via MERGE statement for each table so we don't need to
  1163. // generate it for each row we insert.
  1164. //-----------------------------------------------------------------------------
  1165. const char *CSchema::GetMergeStatementTextOnPKWhenNotMatchedInsert()
  1166. {
  1167. if ( m_sMergeStatementTextOnPKWhenNotMatchedInsert.IsEmpty() )
  1168. {
  1169. TSQLCmdStr sBuilder;
  1170. BuildMergeStatementTextOnPKWhenNotMatchedInsert( &sBuilder, GetRecordInfo() );
  1171. m_sMergeStatementTextOnPKWhenNotMatchedInsert.Set( sBuilder );
  1172. }
  1173. return m_sMergeStatementTextOnPKWhenNotMatchedInsert;
  1174. }
  1175. //-----------------------------------------------------------------------------
  1176. // Purpose: Get the number of foreign key constraints defined for the table
  1177. //-----------------------------------------------------------------------------
  1178. int CSchema::GetFKCount()
  1179. {
  1180. return m_VecFKData.Count();
  1181. }
  1182. //-----------------------------------------------------------------------------
  1183. // Purpose: Get data for a foreign key by index (valid for 0...GetFKCount()-1)
  1184. //-----------------------------------------------------------------------------
  1185. FKData_t &CSchema::GetFKData( int iIndex )
  1186. {
  1187. return m_VecFKData[iIndex];
  1188. }
  1189. #ifdef DBGFLAG_VALIDATE
  1190. //-----------------------------------------------------------------------------
  1191. // Purpose: Run a global validation pass on all of our data structures and memory
  1192. // allocations.
  1193. // Input: validator - Our global validator object
  1194. // pchName - Our name (typically a member var in our container)
  1195. //-----------------------------------------------------------------------------
  1196. void CSchema::Validate( CValidator &validator, const char *pchName )
  1197. {
  1198. // 1.
  1199. // Claim our memory
  1200. VALIDATE_SCOPE();
  1201. m_VecField.Validate( validator, "m_VecField" );
  1202. m_VecDeleteField.Validate( validator, "m_VecDeleteField" );
  1203. m_VecRenameField.Validate( validator, "m_VecRenameField" );
  1204. m_VecIndexes.Validate( validator, "m_VecIndexes" );
  1205. m_VecFullTextIndexes.Validate( validator, "m_VecFullTextIndexes" );
  1206. ValidateObj( m_VecFKData );
  1207. FOR_EACH_VEC( m_VecFKData, i )
  1208. {
  1209. ValidateObj( m_VecFKData[i] );
  1210. }
  1211. for ( int iIndex = 0; iIndex < m_VecIndexes.Count(); iIndex++ )
  1212. {
  1213. ValidateObj( m_VecIndexes[iIndex] );
  1214. }
  1215. ValidateObj( m_sInsertStatementText );
  1216. // 2.
  1217. // Validate that our fields make sense
  1218. #if defined(_DEBUG)
  1219. uint32 dubOffset = 0;
  1220. for ( int iField = 0; iField < m_VecField.Count(); iField++ )
  1221. {
  1222. Assert( dubOffset == m_VecField[iField].m_dubOffset );
  1223. dubOffset += m_VecField[iField].m_cubLength;
  1224. }
  1225. #endif // defined(_DEBUG)
  1226. }
  1227. //-----------------------------------------------------------------------------
  1228. // Purpose: Validates that a given record from our table is in a good state.
  1229. // Input: pubRecord - Record to validate
  1230. //-----------------------------------------------------------------------------
  1231. void CSchema::ValidateRecord( uint8 *pubRecord )
  1232. {
  1233. // Make sure each record is in a consistent state
  1234. for ( int iField = 0; iField < m_VecField.Count(); iField++ )
  1235. {
  1236. Field_t &field = m_VecField[iField];
  1237. // Ensure that strings are null-terminated, and that everything after the terminator is 0
  1238. if ( k_EGCSQLType_String == field.m_EType )
  1239. {
  1240. char *pchField = ( char * ) pubRecord + field.m_dubOffset;
  1241. Assert( 0 == pchField[field.m_cubLength - 1] );
  1242. for ( uint32 ich = 0; ich < field.m_cubLength; ich++ )
  1243. {
  1244. if ( 0 == pchField[ich] )
  1245. {
  1246. while ( ich < field.m_cubLength )
  1247. {
  1248. Assert( 0 == pchField[ich] );
  1249. ich++;
  1250. }
  1251. }
  1252. }
  1253. }
  1254. }
  1255. }
  1256. #endif // DBGFLAG_VALIDATE
  1257. } // namespace GCSDK