PageRenderTime 50ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/Aurora/DataManager/DataManagerBase.cs

https://bitbucket.org/VirtualReality/async-sim-testing
C# | 467 lines | 373 code | 62 blank | 32 comment | 45 complexity | ea972f37fe6315cf53a0f4f3f4703349 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.DataManager.Migration;
  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. foreach (
  74. var version in
  75. results.Where(result => result.Trim() != string.Empty)
  76. .Select(result => new Version(result))
  77. .Where(version => highestVersion[0] == null || version > highestVersion[0]))
  78. {
  79. highestVersion[0] = version;
  80. }
  81. return highestVersion[0];
  82. }
  83. return null;
  84. }
  85. public void WriteAuroraVersion(Version version, string MigrationName)
  86. {
  87. if (!TableExists(VERSION_TABLE_NAME))
  88. {
  89. CreateTable(VERSION_TABLE_NAME, new[]
  90. {
  91. new ColumnDefinition
  92. {
  93. Name = COLUMN_VERSION,
  94. Type =
  95. new ColumnTypeDef
  96. {
  97. Type = ColumnType.String,
  98. Size = 100
  99. }
  100. }
  101. }, new IndexDefinition[0]);
  102. }
  103. //Remove previous versions
  104. QueryFilter filter = new QueryFilter();
  105. filter.andFilters[COLUMN_NAME] = MigrationName;
  106. Delete(VERSION_TABLE_NAME, filter);
  107. //Add the new version
  108. Insert(VERSION_TABLE_NAME, new[] {version.ToString(), MigrationName});
  109. }
  110. public void CopyTableToTable(string sourceTableName, string destinationTableName,
  111. ColumnDefinition[] columnDefinitions, IndexDefinition[] indexDefinitions)
  112. {
  113. if (!TableExists(sourceTableName))
  114. {
  115. throw new MigrationOperationException("Cannot copy table to new name, source table does not exist: " +
  116. sourceTableName);
  117. }
  118. if (TableExists(destinationTableName))
  119. {
  120. this.DropTable(destinationTableName);
  121. if (TableExists(destinationTableName))
  122. throw new MigrationOperationException(
  123. "Cannot copy table to new name, table with same name already exists: " + destinationTableName);
  124. }
  125. if (!VerifyTableExists(sourceTableName, columnDefinitions, indexDefinitions))
  126. {
  127. throw new MigrationOperationException(
  128. "Cannot copy table to new name, source table does not match columnDefinitions: " +
  129. destinationTableName);
  130. }
  131. EnsureTableExists(destinationTableName, columnDefinitions, indexDefinitions, null);
  132. CopyAllDataBetweenMatchingTables(sourceTableName, destinationTableName, columnDefinitions, indexDefinitions);
  133. }
  134. public bool VerifyTableExists(string tableName, ColumnDefinition[] columnDefinitions,
  135. IndexDefinition[] indexDefinitions)
  136. {
  137. if (!TableExists(tableName))
  138. {
  139. MainConsole.Instance.Warn("[DataMigrator]: Issue finding table " + tableName +
  140. " when verifing tables exist!");
  141. return false;
  142. }
  143. List<ColumnDefinition> extractedColumns = ExtractColumnsFromTable(tableName);
  144. List<ColumnDefinition> newColumns = new List<ColumnDefinition>(columnDefinitions);
  145. foreach (ColumnDefinition columnDefinition in columnDefinitions)
  146. {
  147. if (!extractedColumns.Contains(columnDefinition))
  148. {
  149. ColumnDefinition thisDef = null;
  150. foreach (ColumnDefinition extractedDefinition in extractedColumns)
  151. {
  152. if (extractedDefinition.Name.ToLower() == columnDefinition.Name.ToLower())
  153. {
  154. thisDef = extractedDefinition;
  155. break;
  156. }
  157. }
  158. //Check to see whether the two tables have the same type, but under different names
  159. if (thisDef != null)
  160. {
  161. if (GetColumnTypeStringSymbol(thisDef.Type) == GetColumnTypeStringSymbol(columnDefinition.Type))
  162. {
  163. continue; //They are the same type, let them go on through
  164. }
  165. else
  166. {
  167. MainConsole.Instance.Warn("Mismatched Column Type on " + tableName + "." + thisDef.Name +
  168. ": " + GetColumnTypeStringSymbol(thisDef.Type) + ", " +
  169. GetColumnTypeStringSymbol(columnDefinition.Type));
  170. }
  171. }
  172. MainConsole.Instance.Warn("[DataMigrator]: Issue verifing table " + tableName + " column " +
  173. columnDefinition.Name +
  174. " when verifing tables exist, problem with new column definitions");
  175. return false;
  176. }
  177. }
  178. foreach (ColumnDefinition columnDefinition in extractedColumns)
  179. {
  180. if (!newColumns.Contains(columnDefinition))
  181. {
  182. ColumnDefinition thisDef =
  183. newColumns.FirstOrDefault(
  184. extractedDefinition => extractedDefinition.Name.ToLower() == columnDefinition.Name.ToLower());
  185. //Check to see whether the two tables have the same type, but under different names
  186. if (thisDef != null)
  187. {
  188. if (GetColumnTypeStringSymbol(thisDef.Type) == GetColumnTypeStringSymbol(columnDefinition.Type))
  189. {
  190. continue; //They are the same type, let them go on through
  191. }
  192. }
  193. MainConsole.Instance.Warn("[DataMigrator]: Issue verifing table " + tableName + " column " +
  194. columnDefinition.Name +
  195. " when verifing tables exist, problem with old column definitions");
  196. return false;
  197. }
  198. }
  199. Dictionary<string, IndexDefinition> ei = ExtractIndicesFromTable(tableName);
  200. List<IndexDefinition> extractedIndices = new List<IndexDefinition>(ei.Count);
  201. foreach (KeyValuePair<string, IndexDefinition> kvp in ei)
  202. {
  203. extractedIndices.Add(kvp.Value);
  204. }
  205. List<IndexDefinition> newIndices = new List<IndexDefinition>(indexDefinitions);
  206. foreach (IndexDefinition indexDefinition in indexDefinitions)
  207. {
  208. if (!extractedIndices.Contains(indexDefinition))
  209. {
  210. IndexDefinition thisDef = null;
  211. foreach (IndexDefinition extractedDefinition in extractedIndices)
  212. {
  213. if (extractedDefinition.Equals(indexDefinition))
  214. {
  215. thisDef = extractedDefinition;
  216. break;
  217. }
  218. }
  219. if (thisDef == null)
  220. {
  221. MainConsole.Instance.Warn("[DataMigrator]: Issue verifing table " + tableName + " index " +
  222. indexDefinition.Type.ToString() + " (" +
  223. string.Join(", ", indexDefinition.Fields) +
  224. ") when verifing tables exist");
  225. return false;
  226. }
  227. }
  228. }
  229. foreach (IndexDefinition indexDefinition in extractedIndices)
  230. {
  231. if (!newIndices.Contains(indexDefinition))
  232. {
  233. IndexDefinition thisDef = null;
  234. foreach (IndexDefinition extractedDefinition in newIndices)
  235. {
  236. if (extractedDefinition.Equals(indexDefinition))
  237. {
  238. thisDef = extractedDefinition;
  239. break;
  240. }
  241. }
  242. if (thisDef == null)
  243. {
  244. MainConsole.Instance.Warn("[DataMigrator]: Issue verifing table " + tableName + " index " +
  245. indexDefinition.Type.ToString() + " (" +
  246. string.Join(", ", indexDefinition.Fields) +
  247. ") when verifing tables exist");
  248. return false;
  249. }
  250. }
  251. }
  252. return true;
  253. }
  254. public void EnsureTableExists(string tableName, ColumnDefinition[] columnDefinitions,
  255. IndexDefinition[] indexDefinitions, Dictionary<string, string> renameColumns)
  256. {
  257. if (TableExists(tableName))
  258. {
  259. if (!VerifyTableExists(tableName, columnDefinitions, indexDefinitions))
  260. {
  261. //throw new MigrationOperationException("Cannot create, table with same name and different columns already exists. This should be fixed in a migration: " + tableName);
  262. UpdateTable(tableName, columnDefinitions, indexDefinitions, renameColumns);
  263. }
  264. return;
  265. }
  266. CreateTable(tableName, columnDefinitions, indexDefinitions);
  267. }
  268. public void RenameTable(string oldTableName, string newTableName)
  269. {
  270. //Make sure that the old one exists and the new one doesn't
  271. if (TableExists(oldTableName) && !TableExists(newTableName))
  272. {
  273. ForceRenameTable(oldTableName, newTableName);
  274. }
  275. }
  276. public abstract void DropTable(string tableName);
  277. #endregion
  278. #region IGenericData members
  279. #region UPDATE
  280. public abstract bool Update(string table, Dictionary<string, object> values,
  281. Dictionary<string, int> incrementValue, QueryFilter queryFilter, uint? start,
  282. uint? count);
  283. #endregion
  284. #region SELECT
  285. public abstract List<string> Query(string[] wantedValue, string table, QueryFilter queryFilter,
  286. Dictionary<string, bool> sort, uint? start, uint? count);
  287. public abstract List<string> QueryFullData(string whereClause, string table, string wantedValue);
  288. public abstract DataReaderConnection QueryData(string whereClause, string table, string wantedValue);
  289. public abstract Dictionary<string, List<string>> QueryNames(string[] keyRow, object[] keyValue, string table,
  290. string wantedValue);
  291. public abstract List<string> Query(string[] wantedValue, QueryTables tables, QueryFilter queryFilter,
  292. Dictionary<string, bool> sort, uint? start, uint? count);
  293. public abstract Dictionary<string, List<string>> QueryNames(string[] keyRow, object[] keyValue,
  294. QueryTables tables, string wantedValue);
  295. public abstract DataReaderConnection QueryData(string whereClause, QueryTables tables, string wantedValue);
  296. public abstract List<string> QueryFullData(string whereClause, QueryTables tables, string wantedValue);
  297. #endregion
  298. #region INSERT
  299. public abstract bool Insert(string table, object[] values);
  300. public abstract bool Insert(string table, Dictionary<string, object> row);
  301. public abstract bool Insert(string table, object[] values, string updateKey, object updateValue);
  302. public abstract bool InsertMultiple(string table, List<object[]> values);
  303. public abstract bool InsertSelect(string tableA, string[] fieldsA, string tableB, string[] valuesB);
  304. #endregion
  305. #region REPLACE INTO
  306. public abstract bool Replace(string table, Dictionary<string, object> row);
  307. #endregion
  308. #region DELETE
  309. public abstract bool DeleteByTime(string table, string key);
  310. public abstract bool Delete(string table, QueryFilter queryFilter);
  311. #endregion
  312. public abstract void ConnectToDatabase(string connectionString, string migratorName, bool validateTables);
  313. public abstract void CloseDatabase(DataReaderConnection connection);
  314. public abstract IGenericData Copy();
  315. public abstract string ConCat(string[] toConcat);
  316. #endregion
  317. public abstract void UpdateTable(string table, ColumnDefinition[] columns, IndexDefinition[] indexDefinitions,
  318. Dictionary<string, string> renameColumns);
  319. public abstract string GetColumnTypeStringSymbol(ColumnTypes type);
  320. public abstract string GetColumnTypeStringSymbol(ColumnTypeDef coldef);
  321. public ColumnTypeDef ConvertTypeToColumnType(string typeString)
  322. {
  323. string tStr = typeString.ToLower();
  324. ColumnTypeDef typeDef = new ColumnTypeDef();
  325. switch (tStr)
  326. {
  327. case "blob":
  328. typeDef.Type = ColumnType.Blob;
  329. break;
  330. case "longblob":
  331. typeDef.Type = ColumnType.LongBlob;
  332. break;
  333. case "date":
  334. typeDef.Type = ColumnType.Date;
  335. break;
  336. case "datetime":
  337. typeDef.Type = ColumnType.DateTime;
  338. break;
  339. case "double":
  340. typeDef.Type = ColumnType.Double;
  341. break;
  342. case "float":
  343. typeDef.Type = ColumnType.Float;
  344. break;
  345. case "text":
  346. typeDef.Type = ColumnType.Text;
  347. break;
  348. case "mediumtext":
  349. typeDef.Type = ColumnType.MediumText;
  350. break;
  351. case "longtext":
  352. typeDef.Type = ColumnType.LongText;
  353. break;
  354. case "uuid":
  355. typeDef.Type = ColumnType.UUID;
  356. break;
  357. case "integer":
  358. typeDef.Type = ColumnType.Integer;
  359. typeDef.Size = 11;
  360. break;
  361. default:
  362. string regexInt = "^int\\((\\d+)\\)( unsigned)?$";
  363. string regexTinyint = "^tinyint\\((\\d+)\\)( unsigned)?$";
  364. string regexChar = "^char\\((\\d+)\\)$";
  365. string regexString = "^varchar\\((\\d+)\\)$";
  366. Dictionary<string, ColumnType> regexChecks = new Dictionary<string, ColumnType>(4);
  367. regexChecks[regexInt] = ColumnType.Integer;
  368. regexChecks[regexTinyint] = ColumnType.TinyInt;
  369. regexChecks[regexChar] = ColumnType.Char;
  370. regexChecks[regexString] = ColumnType.String;
  371. Match type = Regex.Match("foo", "^bar$");
  372. foreach (KeyValuePair<string, ColumnType> regexCheck in regexChecks)
  373. {
  374. type = Regex.Match(tStr, regexCheck.Key);
  375. if (type.Success)
  376. {
  377. typeDef.Type = regexCheck.Value;
  378. break;
  379. }
  380. }
  381. if (type.Success)
  382. {
  383. typeDef.Size = uint.Parse(type.Groups[1].Value);
  384. typeDef.unsigned = (typeDef.Type == ColumnType.Integer || typeDef.Type == ColumnType.TinyInt)
  385. ? (type.Groups.Count == 3 && type.Groups[2].Value == " unsigned")
  386. : false;
  387. break;
  388. }
  389. else
  390. {
  391. throw new Exception(
  392. "You've discovered some type that's not reconized by Aurora, please place the correct conversion in ConvertTypeToColumnType. Type: " +
  393. tStr);
  394. }
  395. }
  396. return typeDef;
  397. }
  398. public abstract void ForceRenameTable(string oldTableName, string newTableName);
  399. protected abstract void CopyAllDataBetweenMatchingTables(string sourceTableName, string destinationTableName,
  400. ColumnDefinition[] columnDefinitions,
  401. IndexDefinition[] indexDefinitions);
  402. protected abstract List<ColumnDefinition> ExtractColumnsFromTable(string tableName);
  403. protected abstract Dictionary<string, IndexDefinition> ExtractIndicesFromTable(string tableName);
  404. }
  405. }