PageRenderTime 48ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 1ms

/VisualStudio/DataConnectionWrapper.cs

https://github.com/rykr/connector-net
C# | 1075 lines | 641 code | 147 blank | 287 comment | 139 complexity | 47c79b8497e166e95b1c9ccfc18c8ce9 MD5 | raw file
Possible License(s): GPL-2.0, AGPL-1.0, MPL-2.0-no-copyleft-exception
  1. // Copyright (C) 2006-2007 MySQL AB
  2. //
  3. // This file is part of MySQL Tools for Visual Studio.
  4. // MySQL Tools for Visual Studio is free software; you can redistribute it
  5. // and/or modify it under the terms of the GNU Lesser General Public
  6. // License version 2.1 as published by the Free Software Foundation
  7. //
  8. // This program is distributed in the hope that it will be useful,
  9. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. // GNU Lesser General Public License for more details.
  12. //
  13. // You should have received a copy of the GNU Lesser General Public License
  14. // along with this program; if not, write to the Free Software
  15. // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA using System;
  16. /*
  17. * This class contains implementation of the DataConnection wrapper.
  18. */
  19. using System;
  20. using System.Collections.Generic;
  21. using System.Text;
  22. using Microsoft.VisualStudio.Data;
  23. using System.Data;
  24. using MySql.Data.VisualStudio.Properties;
  25. using System.Globalization;
  26. using System.Diagnostics;
  27. using MySql.Data.VisualStudio.Utils;
  28. using MySql.Data.VisualStudio.Descriptors;
  29. using System.Data.SqlTypes;
  30. using System.Data.Common;
  31. namespace MySql.Data.VisualStudio
  32. {
  33. /// <summary>
  34. /// This class wraps DataConnection object and exposes set of useful methods.
  35. /// It supports queries execution and connection information extraction.
  36. /// </summary>
  37. public class DataConnectionWrapper
  38. {
  39. #region Initialization
  40. /// <summary>
  41. /// Constructor stores given connection into private variable
  42. /// </summary>
  43. /// <param name="connection"></param>
  44. public DataConnectionWrapper(DataConnection connection)
  45. {
  46. if (connection == null)
  47. throw new ArgumentNullException("connection");
  48. connectionRef = connection;
  49. }
  50. #endregion
  51. #region Public properties
  52. /// <summary>
  53. /// Returns underlying connection object
  54. /// </summary>
  55. public DataConnection Connection
  56. {
  57. get
  58. {
  59. return connectionRef;
  60. }
  61. }
  62. /// <summary>
  63. /// Returns server name for the underlying connection object.
  64. /// </summary>
  65. public string ServerName
  66. {
  67. get
  68. {
  69. // Retrieving connection
  70. DbConnection conn = GetConnection();
  71. try
  72. {
  73. // Ensure the connection is open
  74. EnsureConnectionIsOpen();
  75. return conn.DataSource;
  76. }
  77. finally
  78. {
  79. Connection.UnlockProviderObject();
  80. }
  81. }
  82. }
  83. /// <summary>
  84. /// Returns server version typed as Version class.
  85. /// </summary>
  86. public Version ServerVersion
  87. {
  88. get
  89. {
  90. // Retrieving connection
  91. DbConnection conn = GetConnection();
  92. // Temporary string to receive server version
  93. string versionString;
  94. // Extract server version from connection
  95. try
  96. {
  97. // Ensure the connection is open
  98. EnsureConnectionIsOpen();
  99. versionString = conn.ServerVersion;
  100. }
  101. finally
  102. {
  103. Connection.UnlockProviderObject();
  104. }
  105. return MySqlConnectionSupport.ParseVersionString(versionString);
  106. }
  107. }
  108. /// <summary>
  109. /// Returns default schema for the underlying connection.
  110. /// </summary>
  111. public string Schema
  112. {
  113. get
  114. {
  115. // Retrieving connection
  116. DbConnection conn = GetConnection();
  117. try
  118. {
  119. // Ensure the connection is open
  120. EnsureConnectionIsOpen();
  121. return conn.Database;
  122. }
  123. finally
  124. {
  125. Connection.UnlockProviderObject();
  126. }
  127. }
  128. }
  129. /// <summary>
  130. /// Returns default character set for underlying schema
  131. /// </summary>
  132. public string DefaultEngine
  133. {
  134. get
  135. {
  136. if (String.IsNullOrEmpty(defaultEngineVal))
  137. {
  138. ReadTableEngines();
  139. }
  140. return defaultEngineVal;
  141. }
  142. }
  143. /// <summary>
  144. /// Returns default character set for underlying schema
  145. /// </summary>
  146. public string DefaultCharacterSet
  147. {
  148. get
  149. {
  150. if (String.IsNullOrEmpty(defaultCharacterSetVal))
  151. {
  152. ReadDefaulCharacterSetAndCollationForSchema();
  153. }
  154. return defaultCharacterSetVal;
  155. }
  156. }
  157. /// <summary>
  158. /// Returns default collation for underlying schema
  159. /// </summary>
  160. public string DefaultCollation
  161. {
  162. get
  163. {
  164. if (String.IsNullOrEmpty(defaultCollationVal))
  165. {
  166. ReadDefaulCharacterSetAndCollationForSchema();
  167. }
  168. return defaultCollationVal;
  169. }
  170. }
  171. /// <summary>
  172. /// Returns name of the current user
  173. /// </summary>
  174. public string CurrentUser
  175. {
  176. get
  177. {
  178. return ExecuteScalar("SELECT CURRENT_USER") as string;
  179. }
  180. }
  181. /// <summary>
  182. /// Returns connection string used for this connection in encrypted fasion.
  183. /// </summary>
  184. public string EncryptedConnectionString
  185. {
  186. get
  187. {
  188. Debug.Assert(connectionRef != null, "Connection reference is empty!");
  189. Debug.Assert(!String.IsNullOrEmpty(connectionRef.EncryptedConnectionString), "Connection string is empty!");
  190. return connectionRef.EncryptedConnectionString;
  191. }
  192. }
  193. /// <summary>
  194. /// Returns underlying ObjectChangeEvents object which can be used by to notify about
  195. /// object changes.
  196. /// </summary>
  197. public DataObjectChangeEvents ObjectChangeEvents
  198. {
  199. get
  200. {
  201. Debug.Assert(connectionRef != null, "Connection reference is empty!");
  202. Debug.Assert(connectionRef.ObjectChangeEvents != null, "ObjectChangeEvents is empty!");
  203. return connectionRef.ObjectChangeEvents;
  204. }
  205. }
  206. #endregion
  207. public DbConnection GetOpenConnection()
  208. {
  209. DbConnection c = GetConnection();
  210. EnsureConnectionIsOpen();
  211. return c;
  212. }
  213. public void ReleaseConnection()
  214. {
  215. Connection.UnlockProviderObject();
  216. }
  217. #region Query execution methods
  218. /// <summary>
  219. /// Executes single SELECT query and returns DataTable as a result.
  220. /// </summary>
  221. /// <param name="selectStatement">SELECT statement to execute.</param>
  222. /// <returns>DataTable object with query results.</returns>
  223. public DataTable ExecuteSelectTable(string selectStatement)
  224. {
  225. // Validate inputs
  226. if (String.IsNullOrEmpty(selectStatement))
  227. throw new ArgumentException(Resources.Error_EmptyString, "selectStatement");
  228. Debug.Assert(Connection != null, "Connection is not initialized!");
  229. // Retrieving connection
  230. DbConnection conn = GetConnection();
  231. try
  232. {
  233. // Ensure the connection is open
  234. EnsureConnectionIsOpen();
  235. // Create a command object
  236. DbCommand comm = conn.CreateCommand();
  237. comm.CommandText = selectStatement;
  238. // Create adapter
  239. DbDataAdapter adapter = CreateDataAdapter();
  240. adapter.SelectCommand = comm;
  241. // Extract data
  242. DataTable result = new DataTable();
  243. result.Locale = CultureInfo.InvariantCulture;
  244. adapter.Fill(result);
  245. return result;
  246. }
  247. catch
  248. {
  249. // Try to ping connection after error to force socket recreation
  250. TryToPingConnection(conn);
  251. // Throw error for further processing
  252. throw;
  253. }
  254. finally
  255. {
  256. Connection.UnlockProviderObject();
  257. }
  258. }
  259. public IDataReader ExecuteReader(string query, bool isProcedure, CommandBehavior behavior)
  260. {
  261. // Validate inputs
  262. if (String.IsNullOrEmpty(query))
  263. throw new ArgumentException(Resources.Error_EmptyString, "query");
  264. Debug.Assert(Connection != null, "Connection is not initialized!");
  265. // Retrieving connection
  266. DbConnection conn = GetConnection();
  267. try
  268. {
  269. // Ensure the connection is open
  270. EnsureConnectionIsOpen();
  271. // Create a command object
  272. DbCommand comm = conn.CreateCommand();
  273. comm.CommandText = query;
  274. if (isProcedure)
  275. comm.CommandType = CommandType.StoredProcedure;
  276. return comm.ExecuteReader(behavior);
  277. }
  278. catch
  279. {
  280. // Try to ping connection after error to force socket recreation
  281. TryToPingConnection(conn);
  282. throw;
  283. }
  284. finally
  285. {
  286. Connection.UnlockProviderObject();
  287. }
  288. }
  289. /// <summary>
  290. /// Executes query which doesn’t return table. Usually this
  291. /// query is for modifications.
  292. /// </summary>
  293. /// <param name="query">SQL text of the query to execute/</param>
  294. /// <returns>Scalar result of the query, if any.</returns>
  295. public object ExecuteScalar(string query)
  296. {
  297. // Validate inputs
  298. if (String.IsNullOrEmpty(query))
  299. throw new ArgumentException(Resources.Error_EmptyString, "query");
  300. Debug.Assert(Connection != null, "Connection is not initialized!");
  301. // Retrieving connection
  302. DbConnection conn = GetConnection();
  303. // Transaction to use
  304. DbTransaction transaction = null;
  305. try
  306. {
  307. // Ensure the connection is open
  308. EnsureConnectionIsOpen();
  309. // Start transaction
  310. transaction = conn.BeginTransaction();
  311. // Create a command object
  312. DbCommand comm = conn.CreateCommand();
  313. comm.CommandText = query;
  314. // Set transaction to command
  315. comm.Transaction = transaction;
  316. // Executes
  317. object result = comm.ExecuteScalar();
  318. // Commit transcation
  319. transaction.Commit();
  320. // Return resukts
  321. return result;
  322. }
  323. catch
  324. {
  325. // Try to ping connection after error to force socket recreation
  326. TryToPingConnection(conn);
  327. // Rollback transaction on error
  328. if (transaction != null)
  329. transaction.Rollback();
  330. throw;
  331. }
  332. finally
  333. {
  334. Connection.UnlockProviderObject();
  335. }
  336. }
  337. /// <summary>
  338. /// Saves changes made to table data to the database.
  339. /// </summary>
  340. /// <param name="table">DataTable with changes.</param>
  341. /// <param name="selectQuery">SELECT SQL query which was used to read this table.</param>
  342. /// <returns>Returns true if save was successful and returns false otherwise.</returns>
  343. public bool UpdateTable(DataTable table, string selectQuery)
  344. {
  345. // Validate inputs
  346. if (table == null)
  347. throw new ArgumentNullException("table");
  348. if (String.IsNullOrEmpty(selectQuery))
  349. throw new ArgumentException(Resources.Error_EmptyString, "selectQuery");
  350. Debug.Assert(Connection != null, "Connection is not initialized!");
  351. // Extract changes from table
  352. DataTable changes = table.GetChanges();
  353. // If no changes detected, return true
  354. if (changes == null)
  355. return true;
  356. // Retrieving connection
  357. DbConnection conn = GetConnection();
  358. DbTransaction transaction = null;
  359. try
  360. {
  361. // Ensure the connection is open
  362. EnsureConnectionIsOpen();
  363. // Start transaction
  364. transaction = conn.BeginTransaction();
  365. // Create command object
  366. DbCommand comm = conn.CreateCommand();
  367. comm.CommandText = selectQuery;
  368. comm.Transaction = transaction;
  369. // Create a data adapter and attach it to command
  370. DbDataAdapter adapter = CreateDataAdapter();
  371. adapter.SelectCommand = comm;
  372. // Create a command builder and attach it to adapter
  373. DbCommandBuilder builder = CreateCommandBuilder(adapter);
  374. // Build update commands
  375. // If there are deleted rows, create delete command
  376. DataRow[] selection = table.Select(String.Empty, String.Empty, DataViewRowState.Deleted);
  377. if (selection != null && selection.Length > 0)
  378. adapter.DeleteCommand = builder.GetDeleteCommand();
  379. // If there are modified rows, create update command
  380. selection = table.Select(String.Empty, String.Empty, DataViewRowState.ModifiedCurrent);
  381. if (selection != null && selection.Length > 0)
  382. adapter.UpdateCommand = builder.GetUpdateCommand();
  383. // If there are new rows, create insert command
  384. selection = table.Select(String.Empty, String.Empty, DataViewRowState.Added);
  385. if (selection != null && selection.Length > 0)
  386. adapter.InsertCommand = builder.GetInsertCommand();
  387. // Attach adapter commands to transactions
  388. if (adapter.UpdateCommand != null)
  389. adapter.UpdateCommand.Transaction = transaction;
  390. if (adapter.InsertCommand != null)
  391. adapter.InsertCommand.Transaction = transaction;
  392. if (adapter.DeleteCommand != null)
  393. adapter.DeleteCommand.Transaction = transaction;
  394. // Saves data
  395. adapter.Update(changes);
  396. // Release resources
  397. builder.Dispose();
  398. adapter.Dispose();
  399. // Commit transaction
  400. transaction.Commit();
  401. // Return results
  402. return true;
  403. }
  404. catch
  405. {
  406. // Try to ping connection after error to force socket recreation
  407. TryToPingConnection(conn);
  408. // On any error rolback transaction if any
  409. if (transaction != null)
  410. transaction.Rollback();
  411. throw;
  412. }
  413. finally
  414. {
  415. Connection.UnlockProviderObject();
  416. }
  417. }
  418. #endregion
  419. #region GetSchema access
  420. /// <summary>
  421. /// Reads schema table using conneection GetSchema method.
  422. /// </summary>
  423. /// <param name="collectionName">Name of collection to read.</param>
  424. /// <param name="restrictions">Restrictions to put on.</param>
  425. /// <returns>Returns DataTable for given collection name.</returns>
  426. public DataTable GetSchema(string collectionName, object[] restrictions)
  427. {
  428. Debug.Assert(Connection != null, "Connection is not initialized!");
  429. // Retrieving connection
  430. DbConnection conn = GetConnection();
  431. try
  432. {
  433. // Ensure the connection is open
  434. EnsureConnectionIsOpen();
  435. return SwitchNumbersToInt64(conn.GetSchema(collectionName, BuildStringRestrictions(restrictions)));
  436. }
  437. finally
  438. {
  439. Connection.UnlockProviderObject();
  440. }
  441. }
  442. /// <summary>
  443. /// Builds string restriction array from object restriction array.
  444. /// </summary>
  445. /// <param name="restrictions">Array of restrictions as objects.</param>
  446. /// <returns>Returns string restriction array from object restriction array.</returns>
  447. private static string[] BuildStringRestrictions(object[] restrictions)
  448. {
  449. string[] stringRestrictions;
  450. if (restrictions == null)
  451. {
  452. stringRestrictions = new string[0];
  453. }
  454. else
  455. {
  456. stringRestrictions = new string[restrictions.Length];
  457. for (int i = 0; i < restrictions.Length; i++)
  458. if (restrictions[i] != null)
  459. stringRestrictions[i] = restrictions[i].ToString();
  460. }
  461. return stringRestrictions;
  462. }
  463. /// <summary>
  464. /// Changes types of all numeric columns to Int64. All numeric columns should have
  465. /// identical type from point of view of other system components.
  466. /// </summary>
  467. /// <param name="table">DataTable with source data.</param>
  468. /// <returns>
  469. /// Returns table with same data, but types of all numeric columns set to Int64.
  470. /// </returns>
  471. private static DataTable SwitchNumbersToInt64(DataTable table)
  472. {
  473. // If source is null, return null.
  474. if (table == null)
  475. return null;
  476. // Clone source schema
  477. DataTable clone = table.Clone();
  478. // Change column types to Int64
  479. foreach (DataColumn column in clone.Columns)
  480. {
  481. if (column.DataType == typeof(Int32)
  482. || column.DataType == typeof(Int16)
  483. || column.DataType == typeof(Byte))
  484. {
  485. column.DataType = typeof(Int64);
  486. }
  487. }
  488. // Copy row data
  489. foreach (DataRow row in table.Rows)
  490. clone.Rows.Add(row.ItemArray);
  491. // Accept changes
  492. clone.AcceptChanges();
  493. // Return cloned table
  494. return clone;
  495. }
  496. #endregion
  497. #region Connection information retrieving methods
  498. /// <summary>
  499. /// Returns array with names of supported engines.
  500. /// </summary>
  501. /// <returns>Returns array with names of supported engines.</returns>
  502. public string[] GetEngines()
  503. {
  504. Debug.Assert(characterSetsList != null);
  505. // Read information if not yet availabel
  506. if (enginesList.Count == 0)
  507. ReadTableEngines();
  508. // Build resulting array
  509. string[] result = new string[enginesList.Count];
  510. enginesList.CopyTo(result);
  511. return result;
  512. }
  513. /// <summary>
  514. /// Returns array with names of supported character sets.
  515. /// </summary>
  516. /// <returns>Returns array with names of supported character sets.</returns>
  517. public string[] GetCharacterSets()
  518. {
  519. Debug.Assert(characterSetsList != null);
  520. // Read information if not yet availabel
  521. if (characterSetsList.Count == 0)
  522. ReadAvailabelCharacterSetsAndCollations();
  523. // Build resulting array
  524. string[] result = new string[characterSetsList.Count];
  525. characterSetsList.CopyTo(result);
  526. return result;
  527. }
  528. /// <summary>
  529. /// Returns array with names of supported character sets.
  530. /// </summary>
  531. /// <returns>Returns array with names of supported character sets.</returns>
  532. public string GetCharacterSetForCollation(string collation)
  533. {
  534. if (String.IsNullOrEmpty(collation))
  535. throw new ArgumentException(Resources.Error_EmptyString, "collation");
  536. Debug.Assert(collationsForCharacterSetDictionary != null);
  537. // Read information if not yet availabel
  538. if (collationsForCharacterSetDictionary.Count == 0)
  539. ReadAvailabelCharacterSetsAndCollations();
  540. // TODO: May be, better to create dictionary for reverse mapping.
  541. // Iterates through character set - collation mapping
  542. foreach (KeyValuePair<string, List<string>> mapping in collationsForCharacterSetDictionary)
  543. {
  544. if (String.IsNullOrEmpty(mapping.Key) || mapping.Value == null)
  545. continue;
  546. if (mapping.Value.Contains(collation))
  547. return mapping.Key;
  548. }
  549. // Failed to find character set
  550. Debug.Fail("Collation " + collation + "is unknown!");
  551. return String.Empty;
  552. }
  553. /// <summary>
  554. /// Returns array with names of supported collations.
  555. /// </summary>
  556. /// <returns>Returns array with names of supported collations.</returns>
  557. public string[] GetCollations()
  558. {
  559. Debug.Assert(collationsList != null);
  560. // Read information if not yet availabel
  561. if (collationsList.Count == 0)
  562. ReadAvailabelCharacterSetsAndCollations();
  563. // Build resulting array
  564. string[] result = new string[collationsList.Count];
  565. collationsList.CopyTo(result);
  566. return result;
  567. }
  568. /// <summary>
  569. /// Returns array with names of supported collation for character set.
  570. /// </summary>
  571. /// <param name="characterSet">Character set name to look for collations.</param>
  572. /// <returns>Returns array with names of supported collation for character set.</returns>
  573. public string[] GetCollationsForCharacterSet(string characterSet)
  574. {
  575. if (String.IsNullOrEmpty(characterSet))
  576. throw new ArgumentException(Resources.Error_EmptyString, "characterSet");
  577. Debug.Assert(collationsForCharacterSetDictionary != null);
  578. // Read information if not yet availabel
  579. if (!collationsForCharacterSetDictionary.ContainsKey(characterSet) || collationsForCharacterSetDictionary[characterSet] == null)
  580. ReadAvailabelCharacterSetsAndCollations();
  581. if (!collationsForCharacterSetDictionary.ContainsKey(characterSet) || collationsForCharacterSetDictionary[characterSet] == null)
  582. {
  583. Debug.Fail("Unknown character set '" + characterSet + "'!");
  584. return null;
  585. }
  586. // Build resulting array
  587. string[] result = new string[collationsForCharacterSetDictionary[characterSet].Count];
  588. collationsForCharacterSetDictionary[characterSet].CopyTo(result);
  589. return result;
  590. }
  591. /// <summary>
  592. /// Returns default collation name for the character set.
  593. /// </summary>
  594. /// <param name="characterSet">Character set name to look for collations.</param>
  595. /// <returns>Returns default collation name for the character set.</returns>
  596. public string GetDefaultCollationForCharacterSet(string characterSet)
  597. {
  598. if (String.IsNullOrEmpty(characterSet))
  599. throw new ArgumentException(Resources.Error_EmptyString, "characterSet");
  600. Debug.Assert(defaultCollationsDictionary != null);
  601. // Read information if not yet availabel
  602. if (defaultCollationsDictionary.Count == 0)
  603. ReadAvailabelCharacterSetsAndCollations();
  604. if (!defaultCollationsDictionary.ContainsKey(characterSet))
  605. {
  606. Debug.Fail("Unknown character set '" + characterSet + "'!");
  607. return null;
  608. }
  609. // Return founded string
  610. return defaultCollationsDictionary[characterSet];
  611. }
  612. /// <summary>
  613. /// Returns full status, including status for all table engines.
  614. /// </summary>
  615. /// <returns>Returns full status, including status for all table engines.</returns>
  616. public string GetFullStatus()
  617. {
  618. // Read engines list
  619. string[] engines = GetEngines();
  620. if (engines == null)
  621. return String.Empty;
  622. // Start iteration
  623. StringBuilder result = new StringBuilder();
  624. foreach (string engine in engines)
  625. {
  626. string status;
  627. // Check if engine supports status
  628. if (engine == null || !HasStatus(engine))
  629. continue;
  630. try
  631. {
  632. // Read engine status
  633. status = ExecuteScalar(
  634. String.Format("SHOW ENGINE {0} STATUS", engine)) as string;
  635. }
  636. catch
  637. {
  638. // Can be because of insufitient rights
  639. continue;
  640. }
  641. // Append status to results
  642. result.AppendFormat(Resources.Status_for_Engine, engine);
  643. result.AppendLine();
  644. result.Append(status);
  645. }
  646. // Return result
  647. return result.ToString();
  648. }
  649. #endregion
  650. #region Reading connection information
  651. #region Short names
  652. // Short names for fiels
  653. private const string CollationName = CollationDescriptor.Attributes.Name;
  654. private const string CharSetName = CollationDescriptor.Attributes.CharacterSetName;
  655. private const string IsDefault = CollationDescriptor.Attributes.IsDefault;
  656. #endregion
  657. /// <summary>
  658. /// Reads information about supported table engines
  659. /// </summary>
  660. private void ReadTableEngines()
  661. {
  662. // Read schema aditional information
  663. DataTable table = EngineDescriptor.Enumerate(this, null);
  664. // Exctract default character set and collation names
  665. if (table != null
  666. && table.Columns.Contains(EngineDescriptor.Attributes.Name)
  667. && table.Columns.Contains(EngineDescriptor.Attributes.IsSupported))
  668. {
  669. FillTableEngines(table);
  670. }
  671. else
  672. {
  673. Debug.Fail("Unable to read table engines!");
  674. }
  675. }
  676. /// <summary>
  677. /// Reads information about default character set and collation for schema.
  678. /// </summary>
  679. private void ReadDefaulCharacterSetAndCollationForSchema()
  680. {
  681. // Read schema aditional information
  682. DataTable table = RootDescriptor.Enumerate(this, null);
  683. // Exctract default character set and collation names
  684. if (table != null && table.Rows.Count > 0
  685. && table.Columns.Contains(RootDescriptor.Attributes.DefaultCharset)
  686. && table.Columns.Contains(RootDescriptor.Attributes.DefaultCollation))
  687. {
  688. defaultCharacterSetVal = DataInterpreter.GetString(table.Rows[0], RootDescriptor.Attributes.DefaultCharset);
  689. defaultCollationVal = DataInterpreter.GetString(table.Rows[0], RootDescriptor.Attributes.DefaultCollation);
  690. }
  691. }
  692. /// <summary>
  693. /// Reads information about all availabel character sets and collations.
  694. /// </summary>
  695. protected void ReadAvailabelCharacterSetsAndCollations()
  696. {
  697. // Extract data table with collations
  698. DataTable table = CollationDescriptor.Enumerate(this, null);
  699. if (table == null)
  700. {
  701. Debug.Fail("Failed to read collations!");
  702. return;
  703. }
  704. // Check data table
  705. if (!table.Columns.Contains(CollationName)
  706. || !table.Columns.Contains(CharSetName)
  707. || !table.Columns.Contains(CharSetName))
  708. {
  709. Debug.Fail("One of required collumns is missing!");
  710. return;
  711. }
  712. FillCharSetsAndCollations(table);
  713. }
  714. /// <summary>
  715. /// Extracts information about all availabel table engines from the DataTable.
  716. /// </summary>
  717. /// <param name="table">DataTable object with data</param>
  718. private void FillTableEngines(DataTable table)
  719. {
  720. // Iterate through all engines
  721. foreach (DataRow engine in table.Rows)
  722. {
  723. // Extract values
  724. string name = DataInterpreter.GetString(engine, EngineDescriptor.Attributes.Name);
  725. SqlBoolean isSupported = DataInterpreter.GetSqlBool(engine, EngineDescriptor.Attributes.IsSupported);
  726. if (engine["Support"].Equals("DISABLED"))
  727. isSupported = false;
  728. // Validate name
  729. if (String.IsNullOrEmpty(name))
  730. {
  731. Debug.Fail("Empty engine name!");
  732. continue;
  733. }
  734. // Check if engine is not supported
  735. if (isSupported.IsFalse)
  736. continue;
  737. // Replacing MRG_MyISAM by more readable MERGE
  738. if (DataInterpreter.CompareInvariant(name, TableDescriptor.MRG_MyISAM))
  739. name = TableDescriptor.MERGE;
  740. // Default engine founded (not YES and not NO - DEFAULT)
  741. if (isSupported.IsNull)
  742. {
  743. Debug.Assert(String.IsNullOrEmpty(defaultEngineVal), "Duplicated default engine!");
  744. defaultEngineVal = name;
  745. }
  746. // Add engine to collaection
  747. if (!enginesList.Contains(name))
  748. enginesList.Add(name);
  749. }
  750. }
  751. /// <summary>
  752. /// Extracts information about all availabel character sets and collations from
  753. /// the DataTable.
  754. /// </summary>
  755. /// <param name="table">DataTable object with data</param>
  756. private void FillCharSetsAndCollations(DataTable table)
  757. {
  758. // Iterate through all collations
  759. foreach (DataRow row in table.Rows)
  760. {
  761. // Extract collation and character set name
  762. string collation = DataInterpreter.GetString(row, CollationName);
  763. string charSet = DataInterpreter.GetString(row, CharSetName);
  764. // Validate names
  765. if (String.IsNullOrEmpty(collation) ||
  766. String.IsNullOrEmpty(charSet))
  767. {
  768. Debug.Fail("Empty collation or character set name!");
  769. continue;
  770. }
  771. // Add character set to list if not already there
  772. if (!characterSetsList.Contains(charSet))
  773. characterSetsList.Add(charSet);
  774. // Add collation to list
  775. Debug.Assert(!collationsList.Contains(collation), "Dublicate collation name founded!");
  776. if (!collationsList.Contains(collation))
  777. collationsList.Add(collation);
  778. // Create entry in the collationsForCharacterSet dictionary
  779. if (!collationsForCharacterSetDictionary.ContainsKey(charSet) || collationsForCharacterSetDictionary[charSet] == null)
  780. collationsForCharacterSetDictionary[charSet] = new List<string>();
  781. List<string> collForCharSet = collationsForCharacterSetDictionary[charSet];
  782. // Add new collation for character set
  783. Debug.Assert(collForCharSet != null &&
  784. !collForCharSet.Contains(collation),
  785. "Empty collation list or dublicate collation!");
  786. if (!collForCharSet.Contains(collation))
  787. collForCharSet.Add(collation);
  788. // If collation is default, add it to the defaultCollations dictionary
  789. if (DataInterpreter.GetSqlBool(row, IsDefault))
  790. {
  791. Debug.Assert(!defaultCollationsDictionary.ContainsKey(charSet), "Default collation already defined!");
  792. defaultCollationsDictionary[charSet] = collation;
  793. }
  794. }
  795. }
  796. #endregion
  797. #region Private methods
  798. /// <summary>
  799. /// Method, which tries to Ping specified connection
  800. /// </summary>
  801. /// <param name="connection">Inkoming parameter. Connection to Ping</param>
  802. /// <returns>
  803. /// If specified connection's type contains method Pind,
  804. /// method returns it's result. If not - method returnes false
  805. /// </returns>
  806. private bool TryToPingConnection(IDbConnection connection)
  807. {
  808. // Check if we have connection
  809. if (connection == null)
  810. {
  811. Debug.Fail("Empty connection reference in ping method!");
  812. return false;
  813. }
  814. // Call connection support to ping
  815. return MySqlConnectionSupport.TryToPingConnection(connection);
  816. }
  817. /// <summary>
  818. /// Returns reference to MySqlConnection for given DataConnection object.
  819. /// WARNING: In successful case looks provider object! Call
  820. /// UnlockProviderObject later in you code.
  821. /// </summary>
  822. /// <returns>Underlying MySqlConnection object.</returns>
  823. private DbConnection GetConnection()
  824. {
  825. Debug.Assert(Connection != null, "Connection is not initialized!");
  826. DbConnection conn = Connection.GetLockedProviderObject() as DbConnection;
  827. Debug.Assert(conn != null, "The underlying connection is not the correct type.");
  828. if (conn == null)
  829. {
  830. Connection.UnlockProviderObject();
  831. throw new ArgumentException(Resources.Error_InvalidConnection, "connection");
  832. }
  833. return conn;
  834. }
  835. /// <summary>
  836. /// Checks connection state and opens it, if necessary.
  837. /// </summary>
  838. private void EnsureConnectionIsOpen()
  839. {
  840. Debug.Assert(Connection != null, "Connection is not initialized!");
  841. if (Connection.State != DataConnectionState.Open)
  842. Connection.Open();
  843. }
  844. /// <summary>
  845. /// Returns true if given engine has status.
  846. /// </summary>
  847. /// <param name="engine">Engine name to check.</param>
  848. /// <returns>Returns true if given engine has status.</returns>
  849. private bool HasStatus(string engine)
  850. {
  851. foreach (string candidate in HasStatusList)
  852. if (DataInterpreter.CompareInvariant(engine, candidate))
  853. return true;
  854. return false;
  855. }
  856. /// <summary>
  857. /// Returns DbProviderFactory used to interact with data provider
  858. /// </summary>
  859. private DbProviderFactory Factory
  860. {
  861. get
  862. {
  863. if (factoryRef != null)
  864. return factoryRef;
  865. try
  866. {
  867. factoryRef = DbProviderFactories.GetFactory(
  868. MySqlConnectionProperties.Names.InvariantProviderName);
  869. Debug.Assert(factoryRef != null, "Empty DbProviderFactory!");
  870. return factoryRef;
  871. }
  872. catch
  873. {
  874. Debug.Fail("Failed to create DbProviderFactory!");
  875. throw;
  876. }
  877. }
  878. }
  879. /// <summary>
  880. /// Returns newly created data adapter.
  881. /// </summary>
  882. /// <returns>Returns newly created data adapter.</returns>
  883. private DbDataAdapter CreateDataAdapter()
  884. {
  885. if (Factory == null)
  886. return null;
  887. // Create adapter
  888. DbDataAdapter adapter = Factory.CreateDataAdapter();
  889. Debug.Assert(adapter != null, "Failed to create data adapter!");
  890. return adapter;
  891. }
  892. /// <summary>
  893. /// Returns new command builder connected to the given data adapter.
  894. /// </summary>
  895. /// <param name="adapter">Data adapter to connect.</param>
  896. /// <returns>Returns new command builder connected to the given data adapter.</returns>
  897. public DbCommandBuilder CreateCommandBuilder(DbDataAdapter adapter)
  898. {
  899. if (Factory == null)
  900. return null;
  901. // Create builder
  902. DbCommandBuilder builder = Factory.CreateCommandBuilder();
  903. Debug.Assert(builder != null, "Failed to create command builder!");
  904. // Connects to data adapter
  905. builder.DataAdapter = adapter;
  906. return builder;
  907. }
  908. #endregion
  909. #region Private constants
  910. /// <summary>
  911. /// List of engines which have status.
  912. /// </summary>
  913. private readonly string[] HasStatusList = new string[] { "InnoDB" };
  914. #endregion
  915. #region Private variables to store properties and connection information
  916. private string defaultCharacterSetVal;
  917. private string defaultCollationVal;
  918. private readonly List<string> characterSetsList = new List<string>();
  919. private readonly List<string> collationsList = new List<string>();
  920. private readonly Dictionary<string, List<string>> collationsForCharacterSetDictionary = new Dictionary<string, List<string>>();
  921. private readonly Dictionary<string, string> defaultCollationsDictionary = new Dictionary<string, string>();
  922. private readonly DataConnection connectionRef;
  923. private readonly List<string> enginesList = new List<string>();
  924. private string defaultEngineVal = null;
  925. private DbProviderFactory factoryRef;
  926. #endregion
  927. }
  928. }