PageRenderTime 45ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/Aurora/DataManager/DataManagerBase.cs

https://bitbucket.org/VirtualReality/software-testing
C# | 492 lines | 399 code | 61 blank | 32 comment | 52 complexity | d15844bc153a1a38fd97bb9bbf596870 MD5 | raw file
  1. /*
  2. * Copyright (c) Contributors, http://aurora-sim.org/
  3. * See CONTRIBUTORS.TXT for a full list of copyright holders.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. * * Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * * Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. * * Neither the name of the Aurora-Sim Project nor the
  13. * names of its contributors may be used to endorse or promote products
  14. * derived from this software without specific prior written permission.
  15. *
  16. * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
  17. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  18. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19. * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
  20. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  21. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  23. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. using System;
  28. using System.Collections.Generic;
  29. using System.Linq;
  30. using System.Text.RegularExpressions;
  31. using Aurora.Framework;
  32. using Aurora.Framework.ConsoleFramework;
  33. using Aurora.Framework.Services;
  34. using Aurora.Framework.Utilities;
  35. namespace Aurora.DataManager
  36. {
  37. public abstract class DataManagerBase : IDataConnector
  38. {
  39. private const string VERSION_TABLE_NAME = "aurora_migrator_version";
  40. private const string COLUMN_NAME = "name";
  41. private const string COLUMN_VERSION = "version";
  42. #region IDataConnector Members
  43. public abstract string Identifier { get; }
  44. public abstract bool TableExists(string table);
  45. public abstract void CreateTable(string table, ColumnDefinition[] columns, IndexDefinition[] indexDefinitions);
  46. public Version GetAuroraVersion(string migratorName)
  47. {
  48. if (!TableExists(VERSION_TABLE_NAME))
  49. {
  50. CreateTable(VERSION_TABLE_NAME, new[]
  51. {
  52. new ColumnDefinition
  53. {
  54. Name = COLUMN_VERSION,
  55. Type = new ColumnTypeDef {Type = ColumnType.Text}
  56. },
  57. new ColumnDefinition
  58. {
  59. Name = COLUMN_NAME,
  60. Type = new ColumnTypeDef {Type = ColumnType.Text}
  61. }
  62. }, new IndexDefinition[0]);
  63. }
  64. Dictionary<string, object> where = new Dictionary<string, object>(1);
  65. where[COLUMN_NAME] = migratorName;
  66. List<string> results = Query(new string[] {COLUMN_VERSION}, VERSION_TABLE_NAME, new QueryFilter
  67. {
  68. andFilters = where
  69. }, null, null, null);
  70. if (results.Count > 0)
  71. {
  72. Version[] highestVersion = {null};
  73. #if (!ISWIN)
  74. foreach (string result in results)
  75. {
  76. if (result.Trim() != string.Empty)
  77. {
  78. var version = new Version(result);
  79. if (highestVersion[0] == null || version > highestVersion[0])
  80. {
  81. highestVersion[0] = version;
  82. }
  83. }
  84. }
  85. #else
  86. foreach (
  87. var version in
  88. results.Where(result => result.Trim() != string.Empty)
  89. .Select(result => new Version(result))
  90. .Where(version => highestVersion[0] == null || version > highestVersion[0]))
  91. {
  92. highestVersion[0] = version;
  93. }
  94. #endif
  95. return highestVersion[0];
  96. }
  97. return null;
  98. }
  99. public void WriteAuroraVersion(Version version, string MigrationName)
  100. {
  101. if (!TableExists(VERSION_TABLE_NAME))
  102. {
  103. CreateTable(VERSION_TABLE_NAME, new[]
  104. {
  105. new ColumnDefinition
  106. {
  107. Name = COLUMN_VERSION,
  108. Type =
  109. new ColumnTypeDef
  110. {
  111. Type = ColumnType.String,
  112. Size = 100
  113. }
  114. }
  115. }, new IndexDefinition[0]);
  116. }
  117. //Remove previous versions
  118. QueryFilter filter = new QueryFilter();
  119. filter.andFilters[COLUMN_NAME] = MigrationName;
  120. Delete(VERSION_TABLE_NAME, filter);
  121. //Add the new version
  122. Insert(VERSION_TABLE_NAME, new[] {version.ToString(), MigrationName});
  123. }
  124. public void CopyTableToTable(string sourceTableName, string destinationTableName,
  125. ColumnDefinition[] columnDefinitions, IndexDefinition[] indexDefinitions)
  126. {
  127. if (!TableExists(sourceTableName))
  128. {
  129. throw new MigrationOperationException("Cannot copy table to new name, source table does not exist: " +
  130. sourceTableName);
  131. }
  132. if (TableExists(destinationTableName))
  133. {
  134. this.DropTable(destinationTableName);
  135. if (TableExists(destinationTableName))
  136. throw new MigrationOperationException(
  137. "Cannot copy table to new name, table with same name already exists: " + destinationTableName);
  138. }
  139. if (!VerifyTableExists(sourceTableName, columnDefinitions, indexDefinitions))
  140. {
  141. throw new MigrationOperationException(
  142. "Cannot copy table to new name, source table does not match columnDefinitions: " +
  143. destinationTableName);
  144. }
  145. EnsureTableExists(destinationTableName, columnDefinitions, indexDefinitions, null);
  146. CopyAllDataBetweenMatchingTables(sourceTableName, destinationTableName, columnDefinitions, indexDefinitions);
  147. }
  148. public bool VerifyTableExists(string tableName, ColumnDefinition[] columnDefinitions,
  149. IndexDefinition[] indexDefinitions)
  150. {
  151. if (!TableExists(tableName))
  152. {
  153. MainConsole.Instance.Warn("[DataMigrator]: Issue finding table " + tableName +
  154. " when verifing tables exist!");
  155. return false;
  156. }
  157. List<ColumnDefinition> extractedColumns = ExtractColumnsFromTable(tableName);
  158. List<ColumnDefinition> newColumns = new List<ColumnDefinition>(columnDefinitions);
  159. foreach (ColumnDefinition columnDefinition in columnDefinitions)
  160. {
  161. if (!extractedColumns.Contains(columnDefinition))
  162. {
  163. ColumnDefinition thisDef = null;
  164. foreach (ColumnDefinition extractedDefinition in extractedColumns)
  165. {
  166. if (extractedDefinition.Name.ToLower() == columnDefinition.Name.ToLower())
  167. {
  168. thisDef = extractedDefinition;
  169. break;
  170. }
  171. }
  172. //Check to see whether the two tables have the same type, but under different names
  173. if (thisDef != null)
  174. {
  175. if (GetColumnTypeStringSymbol(thisDef.Type) == GetColumnTypeStringSymbol(columnDefinition.Type))
  176. {
  177. continue; //They are the same type, let them go on through
  178. }
  179. else
  180. {
  181. MainConsole.Instance.Warn("Mismatched Column Type on " + tableName + "." + thisDef.Name +
  182. ": " + GetColumnTypeStringSymbol(thisDef.Type) + ", " +
  183. GetColumnTypeStringSymbol(columnDefinition.Type));
  184. }
  185. }
  186. MainConsole.Instance.Warn("[DataMigrator]: Issue verifing table " + tableName + " column " +
  187. columnDefinition.Name +
  188. " when verifing tables exist, problem with new column definitions");
  189. return false;
  190. }
  191. }
  192. foreach (ColumnDefinition columnDefinition in extractedColumns)
  193. {
  194. if (!newColumns.Contains(columnDefinition))
  195. {
  196. #if (!ISWIN)
  197. ColumnDefinition thisDef = null;
  198. foreach (ColumnDefinition extractedDefinition in newColumns)
  199. {
  200. if (extractedDefinition.Name.ToLower() == columnDefinition.Name.ToLower())
  201. {
  202. thisDef = extractedDefinition;
  203. break;
  204. }
  205. }
  206. #else
  207. ColumnDefinition thisDef =
  208. newColumns.FirstOrDefault(
  209. extractedDefinition => extractedDefinition.Name.ToLower() == columnDefinition.Name.ToLower());
  210. #endif
  211. //Check to see whether the two tables have the same type, but under different names
  212. if (thisDef != null)
  213. {
  214. if (GetColumnTypeStringSymbol(thisDef.Type) == GetColumnTypeStringSymbol(columnDefinition.Type))
  215. {
  216. continue; //They are the same type, let them go on through
  217. }
  218. }
  219. MainConsole.Instance.Warn("[DataMigrator]: Issue verifing table " + tableName + " column " +
  220. columnDefinition.Name +
  221. " when verifing tables exist, problem with old column definitions");
  222. return false;
  223. }
  224. }
  225. Dictionary<string, IndexDefinition> ei = ExtractIndicesFromTable(tableName);
  226. List<IndexDefinition> extractedIndices = new List<IndexDefinition>(ei.Count);
  227. foreach (KeyValuePair<string, IndexDefinition> kvp in ei)
  228. {
  229. extractedIndices.Add(kvp.Value);
  230. }
  231. List<IndexDefinition> newIndices = new List<IndexDefinition>(indexDefinitions);
  232. foreach (IndexDefinition indexDefinition in indexDefinitions)
  233. {
  234. if (!extractedIndices.Contains(indexDefinition))
  235. {
  236. IndexDefinition thisDef = null;
  237. foreach (IndexDefinition extractedDefinition in extractedIndices)
  238. {
  239. if (extractedDefinition.Equals(indexDefinition))
  240. {
  241. thisDef = extractedDefinition;
  242. break;
  243. }
  244. }
  245. if (thisDef == null)
  246. {
  247. MainConsole.Instance.Warn("[DataMigrator]: Issue verifing table " + tableName + " index " +
  248. indexDefinition.Type.ToString() + " (" +
  249. string.Join(", ", indexDefinition.Fields) +
  250. ") when verifing tables exist");
  251. return false;
  252. }
  253. }
  254. }
  255. foreach (IndexDefinition indexDefinition in extractedIndices)
  256. {
  257. if (!newIndices.Contains(indexDefinition))
  258. {
  259. IndexDefinition thisDef = null;
  260. foreach (IndexDefinition extractedDefinition in newIndices)
  261. {
  262. if (extractedDefinition.Equals(indexDefinition))
  263. {
  264. thisDef = extractedDefinition;
  265. break;
  266. }
  267. }
  268. if (thisDef == null)
  269. {
  270. MainConsole.Instance.Warn("[DataMigrator]: Issue verifing table " + tableName + " index " +
  271. indexDefinition.Type.ToString() + " (" +
  272. string.Join(", ", indexDefinition.Fields) +
  273. ") when verifing tables exist");
  274. return false;
  275. }
  276. }
  277. }
  278. return true;
  279. }
  280. public void EnsureTableExists(string tableName, ColumnDefinition[] columnDefinitions,
  281. IndexDefinition[] indexDefinitions, Dictionary<string, string> renameColumns)
  282. {
  283. if (TableExists(tableName))
  284. {
  285. if (!VerifyTableExists(tableName, columnDefinitions, indexDefinitions))
  286. {
  287. //throw new MigrationOperationException("Cannot create, table with same name and different columns already exists. This should be fixed in a migration: " + tableName);
  288. UpdateTable(tableName, columnDefinitions, indexDefinitions, renameColumns);
  289. }
  290. return;
  291. }
  292. CreateTable(tableName, columnDefinitions, indexDefinitions);
  293. }
  294. public void RenameTable(string oldTableName, string newTableName)
  295. {
  296. //Make sure that the old one exists and the new one doesn't
  297. if (TableExists(oldTableName) && !TableExists(newTableName))
  298. {
  299. ForceRenameTable(oldTableName, newTableName);
  300. }
  301. }
  302. public abstract void DropTable(string tableName);
  303. #endregion
  304. #region IGenericData members
  305. #region UPDATE
  306. public abstract bool Update(string table, Dictionary<string, object> values,
  307. Dictionary<string, int> incrementValue, QueryFilter queryFilter, uint? start,
  308. uint? count);
  309. #endregion
  310. #region SELECT
  311. public abstract List<string> Query(string[] wantedValue, string table, QueryFilter queryFilter,
  312. Dictionary<string, bool> sort, uint? start, uint? count);
  313. public abstract List<string> QueryFullData(string whereClause, string table, string wantedValue);
  314. public abstract DataReaderConnection QueryData(string whereClause, string table, string wantedValue);
  315. public abstract Dictionary<string, List<string>> QueryNames(string[] keyRow, object[] keyValue, string table,
  316. string wantedValue);
  317. public abstract List<string> Query(string[] wantedValue, QueryTables tables, QueryFilter queryFilter,
  318. Dictionary<string, bool> sort, uint? start, uint? count);
  319. public abstract Dictionary<string, List<string>> QueryNames(string[] keyRow, object[] keyValue,
  320. QueryTables tables, string wantedValue);
  321. public abstract DataReaderConnection QueryData(string whereClause, QueryTables tables, string wantedValue);
  322. public abstract List<string> QueryFullData(string whereClause, QueryTables tables, string wantedValue);
  323. #endregion
  324. #region INSERT
  325. public abstract bool Insert(string table, object[] values);
  326. public abstract bool Insert(string table, Dictionary<string, object> row);
  327. public abstract bool Insert(string table, object[] values, string updateKey, object updateValue);
  328. public abstract bool InsertMultiple(string table, List<object[]> values);
  329. public abstract bool InsertSelect(string tableA, string[] fieldsA, string tableB, string[] valuesB);
  330. #endregion
  331. #region REPLACE INTO
  332. public abstract bool Replace(string table, Dictionary<string, object> row);
  333. #endregion
  334. #region DELETE
  335. public abstract bool DeleteByTime(string table, string key);
  336. public abstract bool Delete(string table, QueryFilter queryFilter);
  337. #endregion
  338. public abstract void ConnectToDatabase(string connectionString, string migratorName, bool validateTables);
  339. public abstract void CloseDatabase(DataReaderConnection connection);
  340. public abstract IGenericData Copy();
  341. public abstract string ConCat(string[] toConcat);
  342. #endregion
  343. public abstract void UpdateTable(string table, ColumnDefinition[] columns, IndexDefinition[] indexDefinitions,
  344. Dictionary<string, string> renameColumns);
  345. public abstract string GetColumnTypeStringSymbol(ColumnTypes type);
  346. public abstract string GetColumnTypeStringSymbol(ColumnTypeDef coldef);
  347. public ColumnTypeDef ConvertTypeToColumnType(string typeString)
  348. {
  349. string tStr = typeString.ToLower();
  350. ColumnTypeDef typeDef = new ColumnTypeDef();
  351. switch (tStr)
  352. {
  353. case "blob":
  354. typeDef.Type = ColumnType.Blob;
  355. break;
  356. case "longblob":
  357. typeDef.Type = ColumnType.LongBlob;
  358. break;
  359. case "date":
  360. typeDef.Type = ColumnType.Date;
  361. break;
  362. case "datetime":
  363. typeDef.Type = ColumnType.DateTime;
  364. break;
  365. case "double":
  366. typeDef.Type = ColumnType.Double;
  367. break;
  368. case "float":
  369. typeDef.Type = ColumnType.Float;
  370. break;
  371. case "text":
  372. typeDef.Type = ColumnType.Text;
  373. break;
  374. case "mediumtext":
  375. typeDef.Type = ColumnType.MediumText;
  376. break;
  377. case "longtext":
  378. typeDef.Type = ColumnType.LongText;
  379. break;
  380. case "uuid":
  381. typeDef.Type = ColumnType.UUID;
  382. break;
  383. case "integer":
  384. typeDef.Type = ColumnType.Integer;
  385. typeDef.Size = 11;
  386. break;
  387. default:
  388. string regexInt = "^int\\((\\d+)\\)( unsigned)?$";
  389. string regexTinyint = "^tinyint\\((\\d+)\\)( unsigned)?$";
  390. string regexChar = "^char\\((\\d+)\\)$";
  391. string regexString = "^varchar\\((\\d+)\\)$";
  392. Dictionary<string, ColumnType> regexChecks = new Dictionary<string, ColumnType>(4);
  393. regexChecks[regexInt] = ColumnType.Integer;
  394. regexChecks[regexTinyint] = ColumnType.TinyInt;
  395. regexChecks[regexChar] = ColumnType.Char;
  396. regexChecks[regexString] = ColumnType.String;
  397. Match type = Regex.Match("foo", "^bar$");
  398. foreach (KeyValuePair<string, ColumnType> regexCheck in regexChecks)
  399. {
  400. type = Regex.Match(tStr, regexCheck.Key);
  401. if (type.Success)
  402. {
  403. typeDef.Type = regexCheck.Value;
  404. break;
  405. }
  406. }
  407. if (type.Success)
  408. {
  409. typeDef.Size = uint.Parse(type.Groups[1].Value);
  410. typeDef.unsigned = (typeDef.Type == ColumnType.Integer || typeDef.Type == ColumnType.TinyInt)
  411. ? (type.Groups.Count == 3 && type.Groups[2].Value == " unsigned")
  412. : false;
  413. break;
  414. }
  415. else
  416. {
  417. throw new Exception(
  418. "You've discovered some type that's not reconized by Aurora, please place the correct conversion in ConvertTypeToColumnType. Type: " +
  419. tStr);
  420. }
  421. }
  422. return typeDef;
  423. }
  424. public abstract void ForceRenameTable(string oldTableName, string newTableName);
  425. protected abstract void CopyAllDataBetweenMatchingTables(string sourceTableName, string destinationTableName,
  426. ColumnDefinition[] columnDefinitions,
  427. IndexDefinition[] indexDefinitions);
  428. protected abstract List<ColumnDefinition> ExtractColumnsFromTable(string tableName);
  429. protected abstract Dictionary<string, IndexDefinition> ExtractIndicesFromTable(string tableName);
  430. }
  431. }