PageRenderTime 51ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/Aurora/DataManager/DataManagerBase.cs

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