PageRenderTime 85ms CodeModel.GetById 50ms RepoModel.GetById 0ms app.codeStats 1ms

/Aurora/DataManagerPlugins/MySQL/MySQLDataManager.cs

https://bitbucket.org/VirtualReality/async-sim-testing
C# | 1181 lines | 1039 code | 100 blank | 42 comment | 87 complexity | 4560154610a7714f0c1243db723c393b 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 Aurora.DataManager.Migration;
  28. using Aurora.Framework.ConsoleFramework;
  29. using Aurora.Framework.Services;
  30. using Aurora.Framework.Utilities;
  31. using MySql.Data.MySqlClient;
  32. using System;
  33. using System.Collections.Generic;
  34. using System.Data;
  35. using System.Linq;
  36. namespace Aurora.DataManager.MySQL
  37. {
  38. public class MySQLDataLoader : DataManagerBase
  39. {
  40. private string m_connectionString = "";
  41. public override string Identifier
  42. {
  43. get { return "MySQLData"; }
  44. }
  45. #region Database
  46. public override void ConnectToDatabase(string connectionstring, string migratorName, bool validateTables)
  47. {
  48. m_connectionString = connectionstring;
  49. MySqlConnection c = new MySqlConnection(connectionstring);
  50. int subStrA = connectionstring.IndexOf("Database=");
  51. int subStrB = connectionstring.IndexOf(";", subStrA);
  52. string noDatabaseConnector = m_connectionString.Substring(0, subStrA) +
  53. m_connectionString.Substring(subStrB + 1);
  54. retry:
  55. try
  56. {
  57. ExecuteNonQuery(noDatabaseConnector, "create schema IF NOT EXISTS " + c.Database,
  58. new Dictionary<string, object>(), false);
  59. }
  60. catch
  61. {
  62. MainConsole.Instance.Error(
  63. "[MySQLDatabase]: We cannot connect to the MySQL instance you have provided. Please make sure it is online, and then press enter to try again.");
  64. Console.Read();
  65. goto retry;
  66. }
  67. var migrationManager = new MigrationManager(this, migratorName, validateTables);
  68. migrationManager.DetermineOperation();
  69. migrationManager.ExecuteOperation();
  70. }
  71. public void CloseDatabase(MySqlConnection connection)
  72. {
  73. //Interlocked.Decrement (ref m_locked);
  74. //connection.Close();
  75. //connection.Dispose();
  76. }
  77. public override void CloseDatabase(DataReaderConnection conn)
  78. {
  79. if (conn != null && conn.DataReader != null)
  80. conn.DataReader.Close();
  81. //Interlocked.Decrement (ref m_locked);
  82. //m_connection.Close();
  83. //m_connection.Dispose();
  84. }
  85. #endregion
  86. #region Query
  87. public IDataReader Query(string sql, Dictionary<string, object> parameters)
  88. {
  89. try
  90. {
  91. MySqlParameter[] param = new MySqlParameter[parameters.Count];
  92. int i = 0;
  93. foreach (KeyValuePair<string, object> p in parameters)
  94. {
  95. param[i] = new MySqlParameter(p.Key, p.Value);
  96. i++;
  97. }
  98. return MySqlHelper.ExecuteReader(m_connectionString, sql, param);
  99. }
  100. catch (Exception e)
  101. {
  102. MainConsole.Instance.Error("[MySQLDataLoader] Query(" + sql + "), " + e);
  103. return null;
  104. }
  105. }
  106. public void ExecuteNonQuery(string sql, Dictionary<string, object> parameters)
  107. {
  108. ExecuteNonQuery(m_connectionString, sql, parameters);
  109. }
  110. public void ExecuteNonQuery(string connStr, string sql, Dictionary<string, object> parameters)
  111. {
  112. ExecuteNonQuery(connStr, sql, parameters, true);
  113. }
  114. public void ExecuteNonQuery(string connStr, string sql, Dictionary<string, object> parameters, bool spamConsole)
  115. {
  116. try
  117. {
  118. MySqlParameter[] param = new MySqlParameter[parameters.Count];
  119. int i = 0;
  120. foreach (KeyValuePair<string, object> p in parameters)
  121. {
  122. param[i] = new MySqlParameter(p.Key, p.Value);
  123. i++;
  124. }
  125. MySqlHelper.ExecuteNonQuery(connStr, sql, param);
  126. }
  127. catch (Exception e)
  128. {
  129. if (spamConsole)
  130. MainConsole.Instance.Error("[MySQLDataLoader] ExecuteNonQuery(" + sql + "), " + e);
  131. else
  132. throw e;
  133. }
  134. }
  135. public override List<string> QueryFullData(string whereClause, string table, string wantedValue)
  136. {
  137. string query = String.Format("select {0} from {1} {2}", wantedValue, table, whereClause);
  138. return QueryFullData2(query);
  139. }
  140. public override List<string> QueryFullData(string whereClause, QueryTables tables, string wantedValue)
  141. {
  142. string query = string.Format("SELECT {0} FROM {1} {2}", wantedValue, tables.ToSQL(), whereClause);
  143. return QueryFullData2(query);
  144. }
  145. private List<string> QueryFullData2(string query)
  146. {
  147. IDataReader reader = null;
  148. List<string> retVal = new List<string>();
  149. try
  150. {
  151. using (reader = Query(query, new Dictionary<string, object>()))
  152. {
  153. while (reader.Read())
  154. {
  155. for (int i = 0; i < reader.FieldCount; i++)
  156. {
  157. retVal.Add(reader.GetString(i));
  158. }
  159. }
  160. return retVal;
  161. }
  162. }
  163. catch (Exception e)
  164. {
  165. MainConsole.Instance.Error("[MySQLDataLoader] QueryFullData(" + query + "), " + e);
  166. return null;
  167. }
  168. finally
  169. {
  170. try
  171. {
  172. if (reader != null)
  173. {
  174. reader.Close();
  175. //reader.Dispose ();
  176. }
  177. }
  178. catch (Exception e)
  179. {
  180. MainConsole.Instance.Error("[MySQLDataLoader] Query(" + query + "), " + e);
  181. }
  182. }
  183. }
  184. public override DataReaderConnection QueryData(string whereClause, string table, string wantedValue)
  185. {
  186. string query = String.Format("select {0} from {1} {2}", wantedValue, table, whereClause);
  187. return new DataReaderConnection {DataReader = QueryData2(query)};
  188. }
  189. public override DataReaderConnection QueryData(string whereClause, QueryTables tables, string wantedValue)
  190. {
  191. string query = string.Format("SELECT {0} FROM {1} {2}", wantedValue, tables.ToSQL(), whereClause);
  192. return new DataReaderConnection {DataReader = QueryData2(query)};
  193. }
  194. private IDataReader QueryData2(string query)
  195. {
  196. return Query(query, new Dictionary<string, object>());
  197. }
  198. public override List<string> Query(string[] wantedValue, string table, QueryFilter queryFilter,
  199. Dictionary<string, bool> sort, uint? start, uint? count)
  200. {
  201. string query = string.Format("SELECT {0} FROM {1}", string.Join(", ", wantedValue), table);
  202. return Query2(query, queryFilter, sort, start, count);
  203. }
  204. public override List<string> Query(string[] wantedValue, QueryTables tables, QueryFilter queryFilter,
  205. Dictionary<string, bool> sort, uint? start, uint? count)
  206. {
  207. string query = string.Format("SELECT {0} FROM {1}", string.Join(", ", wantedValue), tables.ToSQL());
  208. return Query2(query, queryFilter, sort, start, count);
  209. }
  210. private List<string> Query2(string sqll, QueryFilter queryFilter, Dictionary<string, bool> sort, uint? start,
  211. uint? count)
  212. {
  213. string query = sqll;
  214. Dictionary<string, object> ps = new Dictionary<string, object>();
  215. List<string> retVal = new List<string>();
  216. List<string> parts = new List<string>();
  217. if (queryFilter != null && queryFilter.Count > 0)
  218. {
  219. query += " WHERE " + queryFilter.ToSQL('?', out ps);
  220. }
  221. if (sort != null && sort.Count > 0)
  222. {
  223. parts = new List<string>();
  224. foreach (KeyValuePair<string, bool> sortOrder in sort)
  225. {
  226. parts.Add(string.Format("`{0}` {1}", sortOrder.Key, sortOrder.Value ? "ASC" : "DESC"));
  227. }
  228. query += " ORDER BY " + string.Join(", ", parts.ToArray());
  229. }
  230. if (start.HasValue)
  231. {
  232. query += " LIMIT " + start.Value.ToString();
  233. if (count.HasValue)
  234. {
  235. query += ", " + count.Value.ToString();
  236. }
  237. }
  238. IDataReader reader = null;
  239. int i = 0;
  240. try
  241. {
  242. using (reader = Query(query, ps))
  243. {
  244. while (reader.Read())
  245. {
  246. for (i = 0; i < reader.FieldCount; i++)
  247. {
  248. Type r = reader[i].GetType();
  249. retVal.Add(r == typeof (DBNull) ? null : reader.GetString(i));
  250. }
  251. }
  252. return retVal;
  253. }
  254. }
  255. catch (Exception e)
  256. {
  257. MainConsole.Instance.Error("[MySQLDataLoader] Query(" + query + "), " + e);
  258. return null;
  259. }
  260. finally
  261. {
  262. try
  263. {
  264. if (reader != null)
  265. {
  266. reader.Close();
  267. //reader.Dispose ();
  268. }
  269. }
  270. catch (Exception e)
  271. {
  272. MainConsole.Instance.Error("[MySQLDataLoader] Query(" + query + "), " + e);
  273. }
  274. }
  275. }
  276. /*public override Dictionary<string, List<string>> QueryNames(string[] wantedValue, string table, QueryFilter queryFilter, Dictionary<string, bool> sort, uint? start, uint? count)
  277. {
  278. }*/
  279. public override Dictionary<string, List<string>> QueryNames(string[] keyRow, object[] keyValue, string table,
  280. string wantedValue)
  281. {
  282. string query = String.Format("select {0} from {1} where ", wantedValue, table);
  283. return QueryNames2(keyRow, keyValue, query);
  284. }
  285. public override Dictionary<string, List<string>> QueryNames(string[] keyRow, object[] keyValue,
  286. QueryTables tables, string wantedValue)
  287. {
  288. string query = string.Format("SELECT {0} FROM {1} where ", wantedValue, tables.ToSQL());
  289. return QueryNames2(keyRow, keyValue, query);
  290. }
  291. private Dictionary<string, List<string>> QueryNames2(string[] keyRow, object[] keyValue, string query)
  292. {
  293. IDataReader reader = null;
  294. Dictionary<string, List<string>> retVal = new Dictionary<string, List<string>>();
  295. Dictionary<string, object> ps = new Dictionary<string, object>();
  296. int i = 0;
  297. foreach (object value in keyValue)
  298. {
  299. query += String.Format("{0} = ?{1} and ", keyRow[i], keyRow[i]);
  300. ps["?" + keyRow[i]] = value;
  301. i++;
  302. }
  303. query = query.Remove(query.Length - 5);
  304. try
  305. {
  306. using (reader = Query(query, ps))
  307. {
  308. while (reader.Read())
  309. {
  310. for (i = 0; i < reader.FieldCount; i++)
  311. {
  312. Type r = reader[i].GetType();
  313. AddValueToList(ref retVal, reader.GetName(i),
  314. r == typeof (DBNull) ? null : reader[i].ToString());
  315. }
  316. }
  317. return retVal;
  318. }
  319. }
  320. catch (Exception e)
  321. {
  322. MainConsole.Instance.Error("[MySQLDataLoader] QueryNames(" + query + "), " + e);
  323. return null;
  324. }
  325. finally
  326. {
  327. try
  328. {
  329. if (reader != null)
  330. {
  331. reader.Close();
  332. //reader.Dispose ();
  333. }
  334. }
  335. catch (Exception e)
  336. {
  337. MainConsole.Instance.Error("[MySQLDataLoader] QueryNames(" + query + "), " + e);
  338. }
  339. }
  340. }
  341. private void AddValueToList(ref Dictionary<string, List<string>> dic, string key, string value)
  342. {
  343. if (!dic.ContainsKey(key))
  344. {
  345. dic.Add(key, new List<string>());
  346. }
  347. dic[key].Add(value);
  348. }
  349. #endregion
  350. #region Update
  351. public override bool Update(string table, Dictionary<string, object> values,
  352. Dictionary<string, int> incrementValues, QueryFilter queryFilter, uint? start,
  353. uint? count)
  354. {
  355. if ((values == null || values.Count < 1) && (incrementValues == null || incrementValues.Count < 1))
  356. {
  357. MainConsole.Instance.Warn("Update attempted with no values");
  358. return false;
  359. }
  360. string query = string.Format("UPDATE {0}", table);
  361. Dictionary<string, object> ps = new Dictionary<string, object>();
  362. string filter = "";
  363. if (queryFilter != null && queryFilter.Count > 0)
  364. {
  365. filter = " WHERE " + queryFilter.ToSQL('?', out ps);
  366. }
  367. List<string> parts = new List<string>();
  368. if (values != null)
  369. {
  370. foreach (KeyValuePair<string, object> value in values)
  371. {
  372. string key = "?updateSet_" + value.Key.Replace("`", "");
  373. ps[key] = value.Value;
  374. parts.Add(string.Format("{0} = {1}", value.Key, key));
  375. }
  376. }
  377. if (incrementValues != null)
  378. {
  379. foreach (KeyValuePair<string, int> value in incrementValues)
  380. {
  381. string key = "?updateSet_increment_" + value.Key.Replace("`", "");
  382. ps[key] = value.Value;
  383. parts.Add(string.Format("{0} = {0} + {1}", value.Key, key));
  384. }
  385. }
  386. query += " SET " + string.Join(", ", parts.ToArray()) + filter;
  387. if (start.HasValue)
  388. {
  389. query += " LIMIT " + start.Value.ToString();
  390. if (count.HasValue)
  391. {
  392. query += ", " + count.Value.ToString();
  393. }
  394. }
  395. try
  396. {
  397. ExecuteNonQuery(query, ps);
  398. }
  399. catch (MySqlException e)
  400. {
  401. MainConsole.Instance.Error("[MySQLDataLoader] Update(" + query + "), " + e);
  402. }
  403. return true;
  404. }
  405. #endregion
  406. #region Insert
  407. public override bool InsertMultiple(string table, List<object[]> values)
  408. {
  409. string query = String.Format("insert into {0} select ", table);
  410. Dictionary<string, object> parameters = new Dictionary<string, object>();
  411. int i = 0;
  412. foreach (object[] value in values)
  413. {
  414. foreach (object v in value)
  415. {
  416. parameters[Util.ConvertDecString(i)] = v;
  417. query += "?" + Util.ConvertDecString(i++) + ",";
  418. }
  419. query = query.Remove(query.Length - 1);
  420. query += " union all select ";
  421. }
  422. query = query.Remove(query.Length - (" union all select ").Length);
  423. try
  424. {
  425. ExecuteNonQuery(query, parameters);
  426. }
  427. catch (Exception e)
  428. {
  429. MainConsole.Instance.Error("[MySQLDataLoader] Insert(" + query + "), " + e);
  430. }
  431. return true;
  432. }
  433. public override bool Insert(string table, object[] values)
  434. {
  435. string query = String.Format("insert into {0} values (", table);
  436. Dictionary<string, object> parameters = new Dictionary<string, object>();
  437. int i = 0;
  438. foreach (object o in values)
  439. {
  440. parameters[Util.ConvertDecString(i)] = o;
  441. query += "?" + Util.ConvertDecString(i++) + ",";
  442. }
  443. query = query.Remove(query.Length - 1);
  444. query += ")";
  445. try
  446. {
  447. ExecuteNonQuery(query, parameters);
  448. }
  449. catch (Exception e)
  450. {
  451. MainConsole.Instance.Error("[MySQLDataLoader] Insert(" + query + "), " + e);
  452. }
  453. return true;
  454. }
  455. private bool InsertOrReplace(string table, Dictionary<string, object> row, bool insert)
  456. {
  457. string query = (insert ? "INSERT" : "REPLACE") + " INTO " + table + " (" +
  458. string.Join(", ", row.Keys.ToArray<string>()) + ")";
  459. Dictionary<string, object> ps = new Dictionary<string, object>();
  460. foreach (KeyValuePair<string, object> field in row)
  461. {
  462. string key = "?" +
  463. field.Key.Replace("`", "")
  464. .Replace("(", "_")
  465. .Replace(")", "")
  466. .Replace(" ", "_")
  467. .Replace("-", "minus")
  468. .Replace("+", "add")
  469. .Replace("/", "divide")
  470. .Replace("*", "multiply");
  471. ps[key] = field.Value;
  472. }
  473. query += " VALUES( " + string.Join(", ", ps.Keys.ToArray<string>()) + " )";
  474. try
  475. {
  476. ExecuteNonQuery(query, ps);
  477. }
  478. catch (Exception e)
  479. {
  480. MainConsole.Instance.Error("[MySQLDataLoader] " + (insert ? "Insert" : "Replace") + "(" + query + "), " +
  481. e);
  482. }
  483. return true;
  484. }
  485. public override bool Insert(string table, Dictionary<string, object> row)
  486. {
  487. return InsertOrReplace(table, row, true);
  488. }
  489. public override bool Insert(string table, object[] values, string updateKey, object updateValue)
  490. {
  491. string query = String.Format("insert into {0} VALUES(", table);
  492. Dictionary<string, object> param = new Dictionary<string, object>();
  493. int i = 0;
  494. foreach (object o in values)
  495. {
  496. param["?" + Util.ConvertDecString(i)] = o;
  497. query += "?" + Util.ConvertDecString(i++) + ",";
  498. }
  499. param["?update"] = updateValue;
  500. query = query.Remove(query.Length - 1);
  501. query += String.Format(") ON DUPLICATE KEY UPDATE {0} = ?update", updateKey);
  502. try
  503. {
  504. ExecuteNonQuery(query, param);
  505. }
  506. catch (Exception e)
  507. {
  508. MainConsole.Instance.Error("[MySQLDataLoader] Insert(" + query + "), " + e);
  509. return false;
  510. }
  511. return true;
  512. }
  513. public override bool InsertSelect(string tableA, string[] fieldsA, string tableB, string[] valuesB)
  514. {
  515. string query = string.Format("INSERT INTO {0}{1} SELECT {2} FROM {3}",
  516. tableA,
  517. (fieldsA.Length > 0 ? " (" + string.Join(", ", fieldsA) + ")" : ""),
  518. string.Join(", ", valuesB),
  519. tableB
  520. );
  521. try
  522. {
  523. ExecuteNonQuery(query, new Dictionary<string, object>(0));
  524. }
  525. catch (Exception e)
  526. {
  527. MainConsole.Instance.Error("[MySQLDataLoader] INSERT .. SELECT (" + query + "), " + e);
  528. }
  529. return true;
  530. }
  531. #endregion
  532. #region REPLACE INTO
  533. public override bool Replace(string table, Dictionary<string, object> row)
  534. {
  535. return InsertOrReplace(table, row, false);
  536. }
  537. #endregion
  538. #region Delete
  539. public override bool DeleteByTime(string table, string key)
  540. {
  541. QueryFilter filter = new QueryFilter();
  542. filter.andLessThanEqFilters["(UNIX_TIMESTAMP(`" + key.Replace("`", "") + "`) - UNIX_TIMESTAMP())"] = 0;
  543. return Delete(table, filter);
  544. }
  545. public override bool Delete(string table, QueryFilter queryFilter)
  546. {
  547. Dictionary<string, object> ps = new Dictionary<string, object>();
  548. string query = "DELETE FROM " + table +
  549. (queryFilter != null ? (" WHERE " + queryFilter.ToSQL('?', out ps)) : "");
  550. try
  551. {
  552. ExecuteNonQuery(query, ps);
  553. }
  554. catch (Exception e)
  555. {
  556. MainConsole.Instance.Error("[MySQLDataLoader] Delete(" + query + "), " + e);
  557. return false;
  558. }
  559. return true;
  560. }
  561. #endregion
  562. public override string ConCat(string[] toConcat)
  563. {
  564. string returnValue = toConcat.Aggregate("concat(", (current, s) => current + (s + ","));
  565. return returnValue.Substring(0, returnValue.Length - 1) + ")";
  566. }
  567. #region Tables
  568. public override void CreateTable(string table, ColumnDefinition[] columns, IndexDefinition[] indices)
  569. {
  570. table = table.ToLower();
  571. if (TableExists(table))
  572. {
  573. throw new DataManagerException("Trying to create a table with name of one that already exists.");
  574. }
  575. IndexDefinition primary = null;
  576. foreach (IndexDefinition index in indices)
  577. {
  578. if (index.Type == IndexType.Primary)
  579. {
  580. primary = index;
  581. break;
  582. }
  583. }
  584. List<string> columnDefinition = new List<string>();
  585. foreach (ColumnDefinition column in columns)
  586. {
  587. columnDefinition.Add("`" + column.Name + "` " + GetColumnTypeStringSymbol(column.Type));
  588. }
  589. if (primary != null && primary.Fields.Length > 0)
  590. {
  591. columnDefinition.Add("PRIMARY KEY (`" + string.Join("`, `", primary.Fields) + "`)");
  592. }
  593. List<string> indicesQuery = new List<string>(indices.Length);
  594. foreach (IndexDefinition index in indices)
  595. {
  596. string type = "KEY";
  597. switch (index.Type)
  598. {
  599. case IndexType.Primary:
  600. continue;
  601. case IndexType.Unique:
  602. type = "UNIQUE";
  603. break;
  604. case IndexType.Index:
  605. default:
  606. type = "KEY";
  607. break;
  608. }
  609. indicesQuery.Add(string.Format("{0}( {1} )", type, "`" + string.Join("`, `", index.Fields) + "`"));
  610. }
  611. string query = string.Format("create table " + table + " ( {0} {1}) ",
  612. string.Join(", ", columnDefinition.ToArray()),
  613. indicesQuery.Count > 0
  614. ? ", " + string.Join(", ", indicesQuery.ToArray())
  615. : string.Empty);
  616. try
  617. {
  618. ExecuteNonQuery(query, new Dictionary<string, object>());
  619. }
  620. catch (Exception e)
  621. {
  622. MainConsole.Instance.ErrorFormat("[MySQLDataLoader] CreateTable: {0}", e.ToString());
  623. }
  624. }
  625. public override void UpdateTable(string table, ColumnDefinition[] columns, IndexDefinition[] indices,
  626. Dictionary<string, string> renameColumns)
  627. {
  628. table = table.ToLower();
  629. if (!TableExists(table))
  630. {
  631. throw new DataManagerException("Trying to update a table with name of one that does not exist.");
  632. }
  633. List<ColumnDefinition> oldColumns = ExtractColumnsFromTable(table);
  634. Dictionary<string, ColumnDefinition> removedColumns = new Dictionary<string, ColumnDefinition>();
  635. Dictionary<string, ColumnDefinition> modifiedColumns = new Dictionary<string, ColumnDefinition>();
  636. Dictionary<string, ColumnDefinition> addedColumns =
  637. columns.Where(column => !oldColumns.Contains(column)).ToDictionary(column => column.Name.ToLower());
  638. foreach (ColumnDefinition column in oldColumns.Where(column => !columns.Contains(column)))
  639. {
  640. if (addedColumns.ContainsKey(column.Name.ToLower()))
  641. {
  642. if (column.Name.ToLower() != addedColumns[column.Name.ToLower()].Name.ToLower() ||
  643. column.Type != addedColumns[column.Name.ToLower()].Type)
  644. {
  645. modifiedColumns.Add(column.Name.ToLower(), addedColumns[column.Name.ToLower()]);
  646. }
  647. addedColumns.Remove(column.Name.ToLower());
  648. }
  649. else
  650. {
  651. removedColumns.Add(column.Name.ToLower(), column);
  652. }
  653. }
  654. try
  655. {
  656. foreach (
  657. string query in
  658. addedColumns.Values.Select(
  659. column => "add `" + column.Name + "` " + GetColumnTypeStringSymbol(column.Type) +
  660. " ")
  661. .Select(
  662. addedColumnsQuery =>
  663. string.Format("alter table " + table + " " + addedColumnsQuery)))
  664. {
  665. ExecuteNonQuery(query, new Dictionary<string, object>());
  666. }
  667. foreach (
  668. string query in modifiedColumns.Values.Select(column => "modify column `" + column.Name + "` " +
  669. GetColumnTypeStringSymbol(column.Type) + " ")
  670. .Select(
  671. modifiedColumnsQuery =>
  672. string.Format("alter table " + table + " " + modifiedColumnsQuery))
  673. )
  674. {
  675. ExecuteNonQuery(query, new Dictionary<string, object>());
  676. }
  677. foreach (
  678. string query in
  679. removedColumns.Values.Select(column => "drop `" + column.Name + "` ")
  680. .Select(
  681. droppedColumnsQuery =>
  682. string.Format("alter table " + table + " " + droppedColumnsQuery)))
  683. {
  684. ExecuteNonQuery(query, new Dictionary<string, object>());
  685. }
  686. }
  687. catch (Exception e)
  688. {
  689. MainConsole.Instance.ErrorFormat("[MySQLDataLoader] UpdateTable: {0}", e);
  690. }
  691. Dictionary<string, IndexDefinition> oldIndicesDict = ExtractIndicesFromTable(table);
  692. List<string> removeIndices = new List<string>();
  693. List<string> oldIndexNames = new List<string>(oldIndicesDict.Count);
  694. List<IndexDefinition> oldIndices = new List<IndexDefinition>(oldIndicesDict.Count);
  695. List<IndexDefinition> newIndices = new List<IndexDefinition>();
  696. foreach (KeyValuePair<string, IndexDefinition> oldIndex in oldIndicesDict)
  697. {
  698. oldIndexNames.Add(oldIndex.Key);
  699. oldIndices.Add(oldIndex.Value);
  700. }
  701. int i = 0;
  702. foreach (IndexDefinition oldIndex in oldIndices)
  703. {
  704. bool found = false;
  705. foreach (IndexDefinition newIndex in indices)
  706. {
  707. if (oldIndex.Equals(newIndex))
  708. {
  709. found = true;
  710. break;
  711. }
  712. }
  713. if (!found)
  714. {
  715. removeIndices.Add(oldIndexNames[i]);
  716. }
  717. ++i;
  718. }
  719. foreach (IndexDefinition newIndex in indices)
  720. {
  721. bool found = false;
  722. foreach (IndexDefinition oldIndex in oldIndices)
  723. {
  724. if (oldIndex.Equals(newIndex))
  725. {
  726. found = true;
  727. break;
  728. }
  729. }
  730. if (!found)
  731. {
  732. newIndices.Add(newIndex);
  733. }
  734. }
  735. foreach (string oldIndex in removeIndices)
  736. {
  737. ExecuteNonQuery(string.Format("ALTER TABLE `{0}` DROP INDEX `{1}`", table, oldIndex),
  738. new Dictionary<string, object>());
  739. }
  740. foreach (IndexDefinition newIndex in newIndices)
  741. {
  742. ExecuteNonQuery(
  743. string.Format("ALTER TABLE `{0}` ADD {1} (`{2}`)", table,
  744. newIndex.Type == IndexType.Primary
  745. ? "PRIMARY KEY"
  746. : (newIndex.Type == IndexType.Unique ? "UNIQUE" : "INDEX"),
  747. string.Join("`, `", newIndex.Fields)), new Dictionary<string, object>());
  748. }
  749. }
  750. public override string GetColumnTypeStringSymbol(ColumnTypes type)
  751. {
  752. switch (type)
  753. {
  754. case ColumnTypes.Double:
  755. return "DOUBLE";
  756. case ColumnTypes.Integer11:
  757. return "int(11)";
  758. case ColumnTypes.Integer30:
  759. return "int(30)";
  760. case ColumnTypes.UInteger11:
  761. return "INT(11) UNSIGNED";
  762. case ColumnTypes.UInteger30:
  763. return "INT(30) UNSIGNED";
  764. case ColumnTypes.Char36:
  765. return "char(36)";
  766. case ColumnTypes.Char32:
  767. return "char(32)";
  768. case ColumnTypes.Char5:
  769. return "char(5)";
  770. case ColumnTypes.String:
  771. return "TEXT";
  772. case ColumnTypes.String1:
  773. return "VARCHAR(1)";
  774. case ColumnTypes.String2:
  775. return "VARCHAR(2)";
  776. case ColumnTypes.String10:
  777. return "VARCHAR(10)";
  778. case ColumnTypes.String16:
  779. return "VARCHAR(16)";
  780. case ColumnTypes.String30:
  781. return "VARCHAR(30)";
  782. case ColumnTypes.String32:
  783. return "VARCHAR(32)";
  784. case ColumnTypes.String36:
  785. return "VARCHAR(36)";
  786. case ColumnTypes.String45:
  787. return "VARCHAR(45)";
  788. case ColumnTypes.String50:
  789. return "VARCHAR(50)";
  790. case ColumnTypes.String64:
  791. return "VARCHAR(64)";
  792. case ColumnTypes.String128:
  793. return "VARCHAR(128)";
  794. case ColumnTypes.String100:
  795. return "VARCHAR(100)";
  796. case ColumnTypes.String255:
  797. return "VARCHAR(255)";
  798. case ColumnTypes.String512:
  799. return "VARCHAR(512)";
  800. case ColumnTypes.String1024:
  801. return "VARCHAR(1024)";
  802. case ColumnTypes.String8196:
  803. return "VARCHAR(8196)";
  804. case ColumnTypes.Text:
  805. return "TEXT";
  806. case ColumnTypes.MediumText:
  807. return "MEDIUMTEXT";
  808. case ColumnTypes.LongText:
  809. return "LONGTEXT";
  810. case ColumnTypes.Blob:
  811. return "blob";
  812. case ColumnTypes.LongBlob:
  813. return "longblob";
  814. case ColumnTypes.Date:
  815. return "DATE";
  816. case ColumnTypes.DateTime:
  817. return "DATETIME";
  818. case ColumnTypes.Float:
  819. return "float";
  820. case ColumnTypes.TinyInt1:
  821. return "TINYINT(1)";
  822. case ColumnTypes.TinyInt4:
  823. return "TINYINT(4)";
  824. default:
  825. throw new DataManagerException("Unknown column type.");
  826. }
  827. }
  828. public override string GetColumnTypeStringSymbol(ColumnTypeDef coldef)
  829. {
  830. string symbol;
  831. switch (coldef.Type)
  832. {
  833. case ColumnType.Blob:
  834. symbol = "BLOB";
  835. break;
  836. case ColumnType.LongBlob:
  837. symbol = "LONGBLOB";
  838. break;
  839. case ColumnType.Boolean:
  840. symbol = "TINYINT(1)";
  841. break;
  842. case ColumnType.Char:
  843. symbol = "CHAR(" + coldef.Size + ")";
  844. break;
  845. case ColumnType.Date:
  846. symbol = "DATE";
  847. break;
  848. case ColumnType.DateTime:
  849. symbol = "DATETIME";
  850. break;
  851. case ColumnType.Double:
  852. symbol = "DOUBLE";
  853. break;
  854. case ColumnType.Float:
  855. symbol = "FLOAT";
  856. break;
  857. case ColumnType.Integer:
  858. symbol = "INT(" + coldef.Size + ")" + (coldef.unsigned ? " unsigned" : "");
  859. break;
  860. case ColumnType.TinyInt:
  861. symbol = "TINYINT(" + coldef.Size + ")" + (coldef.unsigned ? " unsigned" : "");
  862. break;
  863. case ColumnType.String:
  864. symbol = "VARCHAR(" + coldef.Size + ")";
  865. break;
  866. case ColumnType.Text:
  867. symbol = "TEXT";
  868. break;
  869. case ColumnType.MediumText:
  870. symbol = "MEDIUMTEXT";
  871. break;
  872. case ColumnType.LongText:
  873. symbol = "LONGTEXT";
  874. break;
  875. case ColumnType.UUID:
  876. symbol = "CHAR(36)";
  877. break;
  878. default:
  879. throw new DataManagerException("Unknown column type.");
  880. }
  881. return symbol + (coldef.isNull ? " NULL" : " NOT NULL") +
  882. ((coldef.isNull && coldef.defaultValue == null)
  883. ? " DEFAULT NULL"
  884. : (coldef.defaultValue != null ? " DEFAULT '" + coldef.defaultValue.MySqlEscape() + "'" : "")) +
  885. ((coldef.Type == ColumnType.Integer || coldef.Type == ColumnType.TinyInt) && coldef.auto_increment
  886. ? " AUTO_INCREMENT"
  887. : "");
  888. }
  889. public override void DropTable(string tableName)
  890. {
  891. tableName = tableName.ToLower();
  892. try
  893. {
  894. ExecuteNonQuery(string.Format("drop table {0}", tableName), new Dictionary<string, object>());
  895. }
  896. catch (Exception e)
  897. {
  898. MainConsole.Instance.ErrorFormat("[MySQLDataLoader] DropTable {0}", e.ToString());
  899. }
  900. }
  901. public override void ForceRenameTable(string oldTableName, string newTableName)
  902. {
  903. newTableName = newTableName.ToLower();
  904. try
  905. {
  906. ExecuteNonQuery(string.Format("RENAME TABLE {0} TO {1}", oldTableName, newTableName),
  907. new Dictionary<string, object>());
  908. }
  909. catch (Exception e)
  910. {
  911. MainConsole.Instance.ErrorFormat("[MySQLDataLoader] ForceRenameTable {0}", e.ToString());
  912. }
  913. }
  914. protected override void CopyAllDataBetweenMatchingTables(string sourceTableName, string destinationTableName,
  915. ColumnDefinition[] columnDefinitions,
  916. IndexDefinition[] indexDefinitions)
  917. {
  918. sourceTableName = sourceTableName.ToLower();
  919. destinationTableName = destinationTableName.ToLower();
  920. try
  921. {
  922. ExecuteNonQuery(
  923. string.Format("insert into {0} select * from {1}", destinationTableName, sourceTableName),
  924. new Dictionary<string, object>());
  925. }
  926. catch (Exception e)
  927. {
  928. MainConsole.Instance.ErrorFormat("[MySQLDataLoader] CopyAllDataBetweenMatchingTables", e.ToString());
  929. }
  930. }
  931. public override bool TableExists(string table)
  932. {
  933. IDataReader reader = null;
  934. List<string> retVal = new List<string>();
  935. try
  936. {
  937. using (reader = Query("show tables", new Dictionary<string, object>()))
  938. {
  939. while (reader.Read())
  940. {
  941. for (int i = 0; i < reader.FieldCount; i++)
  942. {
  943. retVal.Add(reader.GetString(i).ToLower());
  944. }
  945. }
  946. }
  947. }
  948. catch (Exception e)
  949. {
  950. MainConsole.Instance.ErrorFormat("[MySQLDataLoader] TableExists: {0}", e.ToString());
  951. }
  952. finally
  953. {
  954. try
  955. {
  956. if (reader != null)
  957. {
  958. reader.Close();
  959. //reader.Dispose ();
  960. }
  961. }
  962. catch (Exception e)
  963. {
  964. MainConsole.Instance.ErrorFormat("[MySQLDataLoader] TableExists: {0}", e.ToString());
  965. }
  966. }
  967. return retVal.Contains(table.ToLower());
  968. }
  969. protected override List<ColumnDefinition> ExtractColumnsFromTable(string tableName)
  970. {
  971. var defs = new List<ColumnDefinition>();
  972. tableName = tableName.ToLower();
  973. IDataReader rdr = null;
  974. try
  975. {
  976. rdr = Query(string.Format("desc {0}", tableName), new Dictionary<string, object>());
  977. while (rdr.Read())
  978. {
  979. var name = rdr["Field"];
  980. //var pk = rdr["Key"];
  981. var type = rdr["Type"];
  982. //var extra = rdr["Extra"];
  983. object defaultValue = rdr["Default"];
  984. ColumnTypeDef typeDef = ConvertTypeToColumnType(type.ToString());
  985. typeDef.isNull = rdr["Null"].ToString() == "YES";
  986. typeDef.auto_increment = rdr["Extra"].ToString().IndexOf("auto_increment") >= 0;
  987. typeDef.defaultValue = defaultValue.GetType() == typeof (System.DBNull)
  988. ? null
  989. : defaultValue.ToString();
  990. defs.Add(new ColumnDefinition
  991. {
  992. Name = name.ToString(),
  993. Type = typeDef,
  994. });
  995. }
  996. }
  997. catch (Exception e)
  998. {
  999. MainConsole.Instance.ErrorFormat("[MySQLDataLoader] ExtractColumnsFromTable: {0}", e.ToString());
  1000. }
  1001. finally
  1002. {
  1003. try
  1004. {
  1005. if (rdr != null)
  1006. {
  1007. rdr.Close();
  1008. //rdr.Dispose ();
  1009. }
  1010. }
  1011. catch (Exception e)
  1012. {
  1013. MainConsole.Instance.DebugFormat("[MySQLDataLoader] ExtractColumnsFromTable: {0}", e.ToString());
  1014. }
  1015. }
  1016. return defs;
  1017. }
  1018. protected override Dictionary<string, IndexDefinition> ExtractIndicesFromTable(string tableName)
  1019. {
  1020. Dictionary<string, IndexDefinition> defs = new Dictionary<string, IndexDefinition>();
  1021. tableName = tableName.ToLower();
  1022. IDataReader rdr = null;
  1023. Dictionary<string, Dictionary<uint, string>> indexLookup =
  1024. new Dictionary<string, Dictionary<uint, string>>();
  1025. Dictionary<string, bool> indexIsUnique = new Dictionary<string, bool>();
  1026. try
  1027. {
  1028. rdr = Query(string.Format("SHOW INDEX IN {0}", tableName), new Dictionary<string, object>());
  1029. while (rdr.Read())
  1030. {
  1031. string name = rdr["Column_name"].ToString();
  1032. bool unique = uint.Parse(rdr["Non_unique"].ToString()) == 0;
  1033. string index = rdr["Key_name"].ToString();
  1034. uint sequence = uint.Parse(rdr["Seq_in_index"].ToString());
  1035. if (indexLookup.ContainsKey(index) == false)
  1036. {
  1037. indexLookup[index] = new Dictionary<uint, string>();
  1038. }
  1039. indexIsUnique[index] = unique;
  1040. indexLookup[index][sequence - 1] = name;
  1041. }
  1042. }
  1043. catch (Exception e)
  1044. {
  1045. MainConsole.Instance.ErrorFormat("[MySQLDataLoader] ExtractIndicesFromTable: {1}", e.ToString());
  1046. }
  1047. finally
  1048. {
  1049. try
  1050. {
  1051. if (rdr != null)
  1052. {
  1053. rdr.Close();
  1054. }
  1055. }
  1056. catch (Exception e)
  1057. {
  1058. MainConsole.Instance.DebugFormat("[MySQLDataLoader] ExtractIndicesFromTable: {0}", e.ToString());
  1059. }
  1060. }
  1061. foreach (KeyValuePair<string, Dictionary<uint, string>> index in indexLookup)
  1062. {
  1063. index.Value.OrderBy(x => x.Key);
  1064. defs[index.Key] = new IndexDefinition
  1065. {
  1066. Fields = index.Value.Values.ToArray<string>(),
  1067. Type =
  1068. (indexIsUnique[index.Key]
  1069. ? (index.Key == "PRIMARY" ? IndexType.Primary : IndexType.Unique)
  1070. : IndexType.Index)
  1071. };
  1072. }
  1073. return defs;
  1074. }
  1075. #endregion
  1076. public override IGenericData Copy()
  1077. {
  1078. return new MySQLDataLoader();
  1079. }
  1080. }
  1081. }