You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1033 lines
36 KiB

  1. // Copyright ?2004, 2014, Oracle and/or its affiliates. All rights reserved.
  2. //
  3. // MySQL Connector/NET is licensed under the terms of the GPLv2
  4. // <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most
  5. // MySQL Connectors. There are special exceptions to the terms and
  6. // conditions of the GPLv2 as it is applied to this software, see the
  7. // FLOSS License Exception
  8. // <http://www.mysql.com/about/legal/licensing/foss-exception.html>.
  9. //
  10. // This program is free software; you can redistribute it and/or modify
  11. // it under the terms of the GNU General Public License as published
  12. // by the Free Software Foundation; version 2 of the License.
  13. //
  14. // This program is distributed in the hope that it will be useful, but
  15. // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  16. // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
  17. // for more details.
  18. //
  19. // You should have received a copy of the GNU General Public License along
  20. // with this program; if not, write to the Free Software Foundation, Inc.,
  21. // 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  22. #if NET40 || NET461
  23. using System;
  24. using System.ComponentModel;
  25. using System.Data;
  26. using System.Data.Common;
  27. using System.Transactions;
  28. using IsolationLevel = System.Data.IsolationLevel;
  29. using System.Drawing;
  30. using System.Drawing.Design;
  31. using System.Text;
  32. using Externals.MySql.Data.Common;
  33. using System.Diagnostics;
  34. using Externals.MySql.Data.MySqlClient.Properties;
  35. using Externals.MySql.Data.MySqlClient.Replication;
  36. using System.Threading.Tasks;
  37. using System.Threading;
  38. namespace Externals.MySql.Data.MySqlClient
  39. {
  40. internal sealed partial class MySqlConnection : IDisposable
  41. {
  42. internal ConnectionState connectionState;
  43. internal Driver driver;
  44. internal bool hasBeenOpen;
  45. private SchemaProvider schemaProvider;
  46. private ProcedureCache procedureCache;
  47. private bool isInUse;
  48. private PerformanceMonitor perfMonitor;
  49. private ExceptionInterceptor exceptionInterceptor;
  50. internal CommandInterceptor commandInterceptor;
  51. private bool isKillQueryConnection;
  52. private string database;
  53. private int commandTimeout;
  54. /// <include file='Externals/MySql.Data-6.9.11/docs/mysqlConnection.xml' path='docs/InfoMessage/*'/>
  55. public event MySqlInfoMessageEventHandler InfoMessage;
  56. private static Cache<string, MySqlConnectionStringBuilder> connectionStringCache =
  57. new Cache<string, MySqlConnectionStringBuilder>(0, 25);
  58. /// <include file='Externals/MySql.Data-6.9.11/docs/mysqlConnection.xml' path='docs/DefaultCtor/*'/>
  59. public MySqlConnection()
  60. {
  61. //TODO: add event data to StateChange docs
  62. Settings = new MySqlConnectionStringBuilder();
  63. database = String.Empty;
  64. }
  65. /// <include file='Externals/MySql.Data-6.9.11/docs/mysqlConnection.xml' path='docs/Ctor1/*'/>
  66. public MySqlConnection(string connectionString)
  67. : this()
  68. {
  69. ConnectionString = connectionString;
  70. }
  71. #region Destructor
  72. ~MySqlConnection()
  73. {
  74. Dispose(false);
  75. }
  76. #endregion
  77. #region Interal Methods & Properties
  78. internal PerformanceMonitor PerfMonitor
  79. {
  80. get { return perfMonitor; }
  81. }
  82. internal ProcedureCache ProcedureCache
  83. {
  84. get { return procedureCache; }
  85. }
  86. internal MySqlConnectionStringBuilder Settings { get; private set; }
  87. internal MySqlDataReader Reader
  88. {
  89. get
  90. {
  91. if (driver == null)
  92. return null;
  93. return driver.reader;
  94. }
  95. set
  96. {
  97. driver.reader = value;
  98. isInUse = driver.reader != null;
  99. }
  100. }
  101. internal void OnInfoMessage(MySqlInfoMessageEventArgs args)
  102. {
  103. if (InfoMessage != null)
  104. {
  105. InfoMessage(this, args);
  106. }
  107. }
  108. internal bool SoftClosed
  109. {
  110. get
  111. {
  112. return (State == ConnectionState.Closed) &&
  113. driver != null &&
  114. driver.CurrentTransaction != null;
  115. }
  116. }
  117. internal bool IsInUse
  118. {
  119. get { return isInUse; }
  120. set { isInUse = value; }
  121. }
  122. #endregion
  123. #region Properties
  124. /// <summary>
  125. /// Returns the id of the server thread this connection is executing on
  126. /// </summary>
  127. [Browsable(false)]
  128. public int ServerThread
  129. {
  130. get { return driver.ThreadID; }
  131. }
  132. /// <summary>
  133. /// Gets the name of the MySQL server to which to connect.
  134. /// </summary>
  135. [Browsable(true)]
  136. public override string DataSource
  137. {
  138. get { return Settings.Server; }
  139. }
  140. /// <include file='Externals/MySql.Data-6.9.11/docs/mysqlConnection.xml' path='docs/ConnectionTimeout/*'/>
  141. [Browsable(true)]
  142. public override int ConnectionTimeout
  143. {
  144. get { return (int)Settings.ConnectionTimeout; }
  145. }
  146. /// <include file='Externals/MySql.Data-6.9.11/docs/mysqlConnection.xml' path='docs/Database/*'/>
  147. [Browsable(true)]
  148. public override string Database
  149. {
  150. get { return database; }
  151. }
  152. /// <summary>
  153. /// Indicates if this connection should use compression when communicating with the server.
  154. /// </summary>
  155. [Browsable(false)]
  156. public bool UseCompression
  157. {
  158. get { return Settings.UseCompression; }
  159. }
  160. /// <include file='Externals/MySql.Data-6.9.11/docs/mysqlConnection.xml' path='docs/State/*'/>
  161. [Browsable(false)]
  162. public override ConnectionState State
  163. {
  164. get { return connectionState; }
  165. }
  166. /// <include file='Externals/MySql.Data-6.9.11/docs/mysqlConnection.xml' path='docs/ServerVersion/*'/>
  167. [Browsable(false)]
  168. public override string ServerVersion
  169. {
  170. get { return driver.Version.ToString(); }
  171. }
  172. /// <include file='Externals/MySql.Data-6.9.11/docs/mysqlConnection.xml' path='docs/ConnectionString/*'/>
  173. [Editor("Externals.MySql.Data.MySqlClient.Design.ConnectionStringTypeEditor,MySqlClient.Design", typeof(UITypeEditor))]
  174. [Browsable(true)]
  175. [Category("Data")]
  176. [Description(
  177. "Information used to connect to a DataSource, such as 'Server=xxx;UserId=yyy;Password=zzz;Database=dbdb'.")]
  178. public override string ConnectionString
  179. {
  180. get
  181. {
  182. // Always return exactly what the user set.
  183. // Security-sensitive information may be removed.
  184. return Settings.GetConnectionString(!hasBeenOpen || Settings.PersistSecurityInfo);
  185. }
  186. set
  187. {
  188. if (State != ConnectionState.Closed)
  189. Throw(new MySqlException(
  190. "Not allowed to change the 'ConnectionString' property while the connection (state=" + State + ")."));
  191. MySqlConnectionStringBuilder newSettings;
  192. lock (connectionStringCache)
  193. {
  194. if (value == null)
  195. newSettings = new MySqlConnectionStringBuilder();
  196. else
  197. {
  198. newSettings = (MySqlConnectionStringBuilder)connectionStringCache[value];
  199. if (null == newSettings)
  200. {
  201. newSettings = new MySqlConnectionStringBuilder(value);
  202. connectionStringCache.Add(value, newSettings);
  203. }
  204. }
  205. }
  206. Settings = newSettings;
  207. if (Settings.Database != null && Settings.Database.Length > 0)
  208. this.database = Settings.Database;
  209. if (driver != null)
  210. driver.Settings = newSettings;
  211. }
  212. }
  213. protected override DbProviderFactory DbProviderFactory
  214. {
  215. get
  216. {
  217. return MySqlClientFactory.Instance;
  218. }
  219. }
  220. public bool IsPasswordExpired { get { return driver.IsPasswordExpired; } }
  221. #endregion
  222. partial void AssertPermissions();
  223. #region Transactions
  224. /// <summary>
  225. /// Enlists in the specified transaction.
  226. /// </summary>
  227. /// <param name="transaction">
  228. /// A reference to an existing <see cref="System.Transactions.Transaction"/> in which to enlist.
  229. /// </param>
  230. public override void EnlistTransaction(Transaction transaction)
  231. {
  232. // enlisting in the null transaction is a noop
  233. if (transaction == null)
  234. return;
  235. // guard against trying to enlist in more than one transaction
  236. if (driver.CurrentTransaction != null)
  237. {
  238. if (driver.CurrentTransaction.BaseTransaction == transaction)
  239. return;
  240. Throw(new MySqlException("Already enlisted"));
  241. }
  242. // now see if we need to swap out drivers. We would need to do this since
  243. // we have to make sure all ops for a given transaction are done on the
  244. // same physical connection.
  245. Driver existingDriver = DriverTransactionManager.GetDriverInTransaction(transaction);
  246. if (existingDriver != null)
  247. {
  248. // we can't allow more than one driver to contribute to the same connection
  249. if (existingDriver.IsInActiveUse)
  250. Throw(new NotSupportedException(Resources.MultipleConnectionsInTransactionNotSupported));
  251. // there is an existing driver and it's not being currently used.
  252. // now we need to see if it is using the same connection string
  253. string text1 = existingDriver.Settings.ConnectionString;
  254. string text2 = Settings.ConnectionString;
  255. if (String.Compare(text1, text2, true) != 0)
  256. Throw(new NotSupportedException(Resources.MultipleConnectionsInTransactionNotSupported));
  257. // close existing driver
  258. // set this new driver as our existing driver
  259. CloseFully();
  260. driver = existingDriver;
  261. }
  262. if (driver.CurrentTransaction == null)
  263. {
  264. MySqlPromotableTransaction t = new MySqlPromotableTransaction(this, transaction);
  265. if (!transaction.EnlistPromotableSinglePhase(t))
  266. Throw(new NotSupportedException(Resources.DistributedTxnNotSupported));
  267. driver.CurrentTransaction = t;
  268. DriverTransactionManager.SetDriverInTransaction(driver);
  269. driver.IsInActiveUse = true;
  270. }
  271. }
  272. /// <include file='Externals/MySql.Data-6.9.11/docs/mysqlConnection.xml' path='docs/BeginTransaction/*'/>
  273. public new MySqlTransaction BeginTransaction()
  274. {
  275. return BeginTransaction(IsolationLevel.RepeatableRead);
  276. }
  277. /// <include file='Externals/MySql.Data-6.9.11/docs/mysqlConnection.xml' path='docs/BeginTransaction1/*'/>
  278. public new MySqlTransaction BeginTransaction(IsolationLevel iso)
  279. {
  280. //TODO: check note in help
  281. if (State != ConnectionState.Open)
  282. Throw(new InvalidOperationException(Resources.ConnectionNotOpen));
  283. // First check to see if we are in a current transaction
  284. if (driver.HasStatus(ServerStatusFlags.InTransaction))
  285. Throw(new InvalidOperationException(Resources.NoNestedTransactions));
  286. MySqlTransaction t = new MySqlTransaction(this, iso);
  287. MySqlCommand cmd = new MySqlCommand("", this);
  288. cmd.CommandText = "SET SESSION TRANSACTION ISOLATION LEVEL ";
  289. switch (iso)
  290. {
  291. case IsolationLevel.ReadCommitted:
  292. cmd.CommandText += "READ COMMITTED";
  293. break;
  294. case IsolationLevel.ReadUncommitted:
  295. cmd.CommandText += "READ UNCOMMITTED";
  296. break;
  297. case IsolationLevel.RepeatableRead:
  298. cmd.CommandText += "REPEATABLE READ";
  299. break;
  300. case IsolationLevel.Serializable:
  301. cmd.CommandText += "SERIALIZABLE";
  302. break;
  303. case IsolationLevel.Chaos:
  304. Throw(new NotSupportedException(Resources.ChaosNotSupported));
  305. break;
  306. case IsolationLevel.Snapshot:
  307. Throw(new NotSupportedException(Resources.SnapshotNotSupported));
  308. break;
  309. }
  310. cmd.ExecuteNonQuery();
  311. cmd.CommandText = "BEGIN";
  312. cmd.ExecuteNonQuery();
  313. return t;
  314. }
  315. #endregion
  316. /// <include file='Externals/MySql.Data-6.9.11/docs/mysqlConnection.xml' path='docs/ChangeDatabase/*'/>
  317. public override void ChangeDatabase(string databaseName)
  318. {
  319. if (databaseName == null || databaseName.Trim().Length == 0)
  320. Throw(new ArgumentException(Resources.ParameterIsInvalid, "databaseName"));
  321. if (State != ConnectionState.Open)
  322. Throw(new InvalidOperationException(Resources.ConnectionNotOpen));
  323. // This lock prevents promotable transaction rollback to run
  324. // in parallel
  325. lock (driver)
  326. {
  327. if (Transaction.Current != null &&
  328. Transaction.Current.TransactionInformation.Status == TransactionStatus.Aborted)
  329. {
  330. Throw(new TransactionAbortedException());
  331. }
  332. // We use default command timeout for SetDatabase
  333. using (new CommandTimer(this, (int)Settings.DefaultCommandTimeout))
  334. {
  335. driver.SetDatabase(databaseName);
  336. }
  337. }
  338. this.database = databaseName;
  339. }
  340. internal void SetState(ConnectionState newConnectionState, bool broadcast)
  341. {
  342. if (newConnectionState == connectionState && !broadcast)
  343. return;
  344. ConnectionState oldConnectionState = connectionState;
  345. connectionState = newConnectionState;
  346. if (broadcast)
  347. OnStateChange(new StateChangeEventArgs(oldConnectionState, connectionState));
  348. }
  349. /// <summary>
  350. /// Ping
  351. /// </summary>
  352. /// <returns></returns>
  353. public bool Ping()
  354. {
  355. if (Reader != null)
  356. Throw(new MySqlException(Resources.DataReaderOpen));
  357. if (driver != null && driver.Ping())
  358. return true;
  359. driver = null;
  360. SetState(ConnectionState.Closed, true);
  361. return false;
  362. }
  363. /// <include file='Externals/MySql.Data-6.9.11/docs/mysqlConnection.xml' path='docs/Open/*'/>
  364. public override void Open()
  365. {
  366. if (State == ConnectionState.Open)
  367. Throw(new InvalidOperationException(Resources.ConnectionAlreadyOpen));
  368. // start up our interceptors
  369. exceptionInterceptor = new ExceptionInterceptor(this);
  370. commandInterceptor = new CommandInterceptor(this);
  371. SetState(ConnectionState.Connecting, true);
  372. AssertPermissions();
  373. // if we are auto enlisting in a current transaction, then we will be
  374. // treating the connection as pooled
  375. if (Settings.AutoEnlist && Transaction.Current != null)
  376. {
  377. driver = DriverTransactionManager.GetDriverInTransaction(Transaction.Current);
  378. if (driver != null &&
  379. (driver.IsInActiveUse ||
  380. !driver.Settings.EquivalentTo(this.Settings)))
  381. Throw(new NotSupportedException(Resources.MultipleConnectionsInTransactionNotSupported));
  382. }
  383. try
  384. {
  385. MySqlConnectionStringBuilder currentSettings = Settings;
  386. // Load balancing
  387. if (ReplicationManager.IsReplicationGroup(Settings.Server))
  388. {
  389. if (driver == null)
  390. {
  391. ReplicationManager.GetNewConnection(Settings.Server, false, this);
  392. }
  393. else
  394. currentSettings = driver.Settings;
  395. }
  396. if (Settings.Pooling)
  397. {
  398. MySqlPool pool = MySqlPoolManager.GetPool(currentSettings);
  399. if (driver == null || !driver.IsOpen)
  400. driver = pool.GetConnection();
  401. procedureCache = pool.ProcedureCache;
  402. }
  403. else
  404. {
  405. if (driver == null || !driver.IsOpen)
  406. driver = Driver.Create(currentSettings);
  407. procedureCache = new ProcedureCache((int)Settings.ProcedureCacheSize);
  408. }
  409. }
  410. catch
  411. {
  412. SetState(ConnectionState.Closed, true);
  413. throw;
  414. }
  415. // if the user is using old syntax, let them know
  416. if (driver.Settings.UseOldSyntax) MySqlTrace.LogWarning(ServerThread, "You are using old syntax that will be removed in future versions");
  417. SetState(ConnectionState.Open, false);
  418. driver.Configure(this);
  419. if (!(driver.SupportsPasswordExpiration && driver.IsPasswordExpired))
  420. {
  421. if (Settings.Database != null && Settings.Database != String.Empty)
  422. ChangeDatabase(Settings.Database);
  423. }
  424. // setup our schema provider
  425. schemaProvider = new ISSchemaProvider(this);
  426. perfMonitor = new PerformanceMonitor(this);
  427. // if we are opening up inside a current transaction, then autoenlist
  428. // TODO: control this with a connection string option
  429. if (Transaction.Current != null && Settings.AutoEnlist)
  430. EnlistTransaction(Transaction.Current);
  431. hasBeenOpen = true;
  432. SetState(ConnectionState.Open, true);
  433. }
  434. /// <include file='Externals/MySql.Data-6.9.11/docs/mysqlConnection.xml' path='docs/CreateCommand/*'/>
  435. public new MySqlCommand CreateCommand()
  436. {
  437. // Return a new instance of a command object.
  438. MySqlCommand c = new MySqlCommand();
  439. c.Connection = this;
  440. return c;
  441. }
  442. /// <summary>
  443. /// Creates a new MySqlConnection object with the exact same ConnectionString value
  444. /// </summary>
  445. /// <returns>A cloned MySqlConnection object</returns>
  446. public object Clone()
  447. {
  448. MySqlConnection clone = new MySqlConnection();
  449. string connectionString = Settings.ConnectionString;
  450. if (connectionString != null)
  451. clone.ConnectionString = connectionString;
  452. return clone;
  453. }
  454. internal void Abort()
  455. {
  456. try
  457. {
  458. driver.Close();
  459. }
  460. catch (Exception ex)
  461. {
  462. MySqlTrace.LogWarning(ServerThread, String.Concat("Error occurred aborting the connection. Exception was: ", ex.Message));
  463. }
  464. finally
  465. {
  466. this.isInUse = false;
  467. }
  468. SetState(ConnectionState.Closed, true);
  469. }
  470. internal void CloseFully()
  471. {
  472. if (Settings.Pooling && driver.IsOpen)
  473. {
  474. // if we are in a transaction, roll it back
  475. if (driver.HasStatus(ServerStatusFlags.InTransaction))
  476. {
  477. MySqlTransaction t = new MySqlTransaction(this, IsolationLevel.Unspecified);
  478. t.Rollback();
  479. }
  480. MySqlPoolManager.ReleaseConnection(driver);
  481. }
  482. else
  483. driver.Close();
  484. driver = null;
  485. }
  486. /// <include file='Externals/MySql.Data-6.9.11/docs/mysqlConnection.xml' path='docs/Close/*'/>
  487. public override void Close()
  488. {
  489. if (driver != null)
  490. driver.IsPasswordExpired = false;
  491. if (State == ConnectionState.Closed) return;
  492. if (Reader != null)
  493. Reader.Close();
  494. // if the reader was opened with CloseConnection then driver
  495. // will be null on the second time through
  496. if (driver != null)
  497. {
  498. if (driver.CurrentTransaction == null)
  499. CloseFully();
  500. else
  501. driver.IsInActiveUse = false;
  502. }
  503. SetState(ConnectionState.Closed, true);
  504. }
  505. internal string CurrentDatabase()
  506. {
  507. if (Database != null && Database.Length > 0)
  508. return Database;
  509. MySqlCommand cmd = new MySqlCommand("SELECT database()", this);
  510. return cmd.ExecuteScalar().ToString();
  511. }
  512. internal void HandleTimeoutOrThreadAbort(Exception ex)
  513. {
  514. bool isFatal = false;
  515. if (isKillQueryConnection)
  516. {
  517. // Special connection started to cancel a query.
  518. // Abort will prevent recursive connection spawning
  519. Abort();
  520. if (ex is TimeoutException)
  521. {
  522. Throw(new MySqlException(Resources.Timeout, true, ex));
  523. }
  524. else
  525. {
  526. return;
  527. }
  528. }
  529. try
  530. {
  531. // Do a fast cancel.The reason behind small values for connection
  532. // and command timeout is that we do not want user to wait longer
  533. // after command has already expired.
  534. // Microsoft's SqlClient seems to be using 5 seconds timeouts
  535. // here as well.
  536. // Read the error packet with "interrupted" message.
  537. CancelQuery(5);
  538. driver.ResetTimeout(5000);
  539. if (Reader != null)
  540. {
  541. Reader.Close();
  542. Reader = null;
  543. }
  544. }
  545. catch (Exception ex2)
  546. {
  547. MySqlTrace.LogWarning(ServerThread, "Could not kill query, " +
  548. " aborting connection. Exception was " + ex2.Message);
  549. Abort();
  550. isFatal = true;
  551. }
  552. if (ex is TimeoutException)
  553. {
  554. Throw(new MySqlException(Resources.Timeout, isFatal, ex));
  555. }
  556. }
  557. public void CancelQuery(int timeout)
  558. {
  559. MySqlConnectionStringBuilder cb = new MySqlConnectionStringBuilder(
  560. Settings.ConnectionString);
  561. cb.Pooling = false;
  562. cb.AutoEnlist = false;
  563. cb.ConnectionTimeout = (uint)timeout;
  564. using (MySqlConnection c = new MySqlConnection(cb.ConnectionString))
  565. {
  566. c.isKillQueryConnection = true;
  567. c.Open();
  568. string commandText = "KILL QUERY " + ServerThread;
  569. MySqlCommand cmd = new MySqlCommand(commandText, c);
  570. cmd.CommandTimeout = timeout;
  571. cmd.ExecuteNonQuery();
  572. }
  573. }
  574. #region Routines for timeout support.
  575. // Problem description:
  576. // Sometimes, ExecuteReader is called recursively. This is the case if
  577. // command behaviors are used and we issue "set sql_select_limit"
  578. // before and after command. This is also the case with prepared
  579. // statements , where we set session variables. In these situations, we
  580. // have to prevent recursive ExecuteReader calls from overwriting
  581. // timeouts set by the top level command.
  582. // To solve the problem, SetCommandTimeout() and ClearCommandTimeout() are
  583. // introduced . Query timeout here is "sticky", that is once set with
  584. // SetCommandTimeout, it only be overwritten after ClearCommandTimeout
  585. // (SetCommandTimeout would return false if it timeout has not been
  586. // cleared).
  587. // The proposed usage pattern of there routines is following:
  588. // When timed operations starts, issue SetCommandTimeout(). When it
  589. // finishes, issue ClearCommandTimeout(), but _only_ if call to
  590. // SetCommandTimeout() was successful.
  591. /// <summary>
  592. /// Sets query timeout. If timeout has been set prior and not
  593. /// yet cleared ClearCommandTimeout(), it has no effect.
  594. /// </summary>
  595. /// <param name="value">timeout in seconds</param>
  596. /// <returns>true if </returns>
  597. internal bool SetCommandTimeout(int value)
  598. {
  599. if (!hasBeenOpen)
  600. // Connection timeout is handled by driver
  601. return false;
  602. if (commandTimeout != 0)
  603. // someone is trying to set a timeout while command is already
  604. // running. It could be for example recursive call to ExecuteReader
  605. // Ignore the request, as only top-level (non-recursive commands)
  606. // can set timeouts.
  607. return false;
  608. if (driver == null)
  609. return false;
  610. commandTimeout = value;
  611. driver.ResetTimeout(commandTimeout * 1000);
  612. return true;
  613. }
  614. /// <summary>
  615. /// Clears query timeout, allowing next SetCommandTimeout() to succeed.
  616. /// </summary>
  617. internal void ClearCommandTimeout()
  618. {
  619. if (!hasBeenOpen)
  620. return;
  621. commandTimeout = 0;
  622. if (driver != null)
  623. {
  624. driver.ResetTimeout(0);
  625. }
  626. }
  627. #endregion
  628. public MySqlSchemaCollection GetSchemaCollection(string collectionName, string[] restrictionValues)
  629. {
  630. if (collectionName == null)
  631. collectionName = SchemaProvider.MetaCollection;
  632. string[] restrictions = schemaProvider.CleanRestrictions(restrictionValues);
  633. MySqlSchemaCollection c = schemaProvider.GetSchema(collectionName, restrictions);
  634. return c;
  635. }
  636. #region Pool Routines
  637. /// <include file='Externals/MySql.Data-6.9.11/docs/mysqlConnection.xml' path='docs/ClearPool/*'/>
  638. public static void ClearPool(MySqlConnection connection)
  639. {
  640. MySqlPoolManager.ClearPool(connection.Settings);
  641. }
  642. /// <include file='Externals/MySql.Data-6.9.11/docs/mysqlConnection.xml' path='docs/ClearAllPools/*'/>
  643. public static void ClearAllPools()
  644. {
  645. MySqlPoolManager.ClearAllPools();
  646. }
  647. #endregion
  648. internal void Throw(Exception ex)
  649. {
  650. throw ex;
  651. }
  652. public new void Dispose()
  653. {
  654. Dispose(true);
  655. GC.SuppressFinalize(this);
  656. }
  657. #region Async
  658. /// <summary>
  659. /// Async version of BeginTransaction
  660. /// </summary>
  661. /// <returns>An object representing the new transaction.</returns>
  662. public Task<MySqlTransaction> BeginTransactionAsync()
  663. {
  664. return BeginTransactionAsync(IsolationLevel.RepeatableRead, CancellationToken.None);
  665. }
  666. public Task<MySqlTransaction> BeginTransactionAsync(CancellationToken cancellationToken)
  667. {
  668. return BeginTransactionAsync(IsolationLevel.RepeatableRead, cancellationToken);
  669. }
  670. /// <summary>
  671. /// Async version of BeginTransaction
  672. /// </summary>
  673. /// <param name="iso">The isolation level under which the transaction should run. </param>
  674. /// <returns>An object representing the new transaction.</returns>
  675. public Task<MySqlTransaction> BeginTransactionAsync(IsolationLevel iso)
  676. {
  677. return BeginTransactionAsync(iso, CancellationToken.None);
  678. }
  679. public Task<MySqlTransaction> BeginTransactionAsync(IsolationLevel iso, CancellationToken cancellationToken)
  680. {
  681. var result = new TaskCompletionSource<MySqlTransaction>();
  682. if (cancellationToken == CancellationToken.None || !cancellationToken.IsCancellationRequested)
  683. {
  684. try
  685. {
  686. MySqlTransaction tranResult = BeginTransaction(iso);
  687. result.SetResult(tranResult);
  688. }
  689. catch (Exception ex)
  690. {
  691. result.SetException(ex);
  692. }
  693. }
  694. else
  695. {
  696. result.SetCanceled();
  697. }
  698. return result.Task;
  699. }
  700. public Task ChangeDataBaseAsync(string databaseName)
  701. {
  702. return ChangeDataBaseAsync(databaseName, CancellationToken.None);
  703. }
  704. /// <summary>
  705. /// Async version of ChangeDataBase
  706. /// </summary>
  707. public Task ChangeDataBaseAsync(string databaseName, CancellationToken cancellationToken)
  708. {
  709. var result = new TaskCompletionSource<bool>();
  710. if (cancellationToken == CancellationToken.None || !cancellationToken.IsCancellationRequested)
  711. {
  712. try
  713. {
  714. ChangeDatabase(databaseName);
  715. result.SetResult(true);
  716. }
  717. catch (Exception ex)
  718. {
  719. result.SetException(ex);
  720. }
  721. }
  722. return result.Task;
  723. }
  724. ///// <summary>
  725. ///// Async version of Open
  726. ///// </summary>
  727. ///// <returns></returns>
  728. //public Task OpenAsync()
  729. //{
  730. // return Task.Run(() =>
  731. // {
  732. // Open();
  733. // });
  734. //}
  735. /// <summary>
  736. /// Async version of Close
  737. /// </summary>
  738. /// <returns></returns>
  739. public Task CloseAsync()
  740. {
  741. return CloseAsync(CancellationToken.None);
  742. }
  743. public Task CloseAsync(CancellationToken cancellationToken)
  744. {
  745. var result = new TaskCompletionSource<bool>();
  746. if (cancellationToken == CancellationToken.None || !cancellationToken.IsCancellationRequested)
  747. {
  748. try
  749. {
  750. Close();
  751. result.SetResult(true);
  752. }
  753. catch (Exception ex)
  754. {
  755. result.SetException(ex);
  756. }
  757. }
  758. else
  759. {
  760. result.SetCanceled();
  761. }
  762. return result.Task;
  763. }
  764. /// <summary>
  765. /// Async version of ClearPool
  766. /// </summary>
  767. /// <param name="connection">The connection associated with the pool to be cleared.</param>
  768. /// <returns></returns>
  769. public Task ClearPoolAsync(MySqlConnection connection)
  770. {
  771. return ClearPoolAsync(connection, CancellationToken.None);
  772. }
  773. public Task ClearPoolAsync(MySqlConnection connection, CancellationToken cancellationToken)
  774. {
  775. var result = new TaskCompletionSource<bool>();
  776. if (cancellationToken == CancellationToken.None || !cancellationToken.IsCancellationRequested)
  777. {
  778. try
  779. {
  780. ClearPool(connection);
  781. result.SetResult(true);
  782. }
  783. catch (Exception ex)
  784. {
  785. result.SetException(ex);
  786. }
  787. }
  788. else
  789. {
  790. result.SetCanceled();
  791. }
  792. return result.Task;
  793. }
  794. /// <summary>
  795. /// Async version of ClearAllPools
  796. /// </summary>
  797. /// <returns></returns>
  798. public Task ClearAllPoolsAsync()
  799. {
  800. return ClearAllPoolsAsync(CancellationToken.None);
  801. }
  802. public Task ClearAllPoolsAsync(CancellationToken cancellationToken)
  803. {
  804. var result = new TaskCompletionSource<bool>();
  805. if (cancellationToken == CancellationToken.None || !cancellationToken.IsCancellationRequested)
  806. {
  807. try
  808. {
  809. ClearAllPools();
  810. result.SetResult(true);
  811. }
  812. catch (Exception ex)
  813. {
  814. result.SetException(ex);
  815. }
  816. }
  817. else
  818. {
  819. result.SetCanceled();
  820. }
  821. return result.Task;
  822. }
  823. /// <summary>
  824. /// Async version of GetSchemaCollection
  825. /// </summary>
  826. /// <param name="collectionName">Name of the collection</param>
  827. /// <param name="restrictionValues">Values to restrict</param>
  828. /// <returns>A schema collection</returns>
  829. public Task<MySqlSchemaCollection> GetSchemaCollectionAsync(string collectionName, string[] restrictionValues)
  830. {
  831. return GetSchemaCollectionAsync(collectionName, restrictionValues, CancellationToken.None);
  832. }
  833. public Task<MySqlSchemaCollection> GetSchemaCollectionAsync(string collectionName, string[] restrictionValues, CancellationToken cancellationToken)
  834. {
  835. var result = new TaskCompletionSource<MySqlSchemaCollection>();
  836. if (cancellationToken == CancellationToken.None || !cancellationToken.IsCancellationRequested)
  837. {
  838. try
  839. {
  840. var schema = GetSchemaCollection(collectionName, restrictionValues);
  841. result.SetResult(schema);
  842. }
  843. catch (Exception ex)
  844. {
  845. result.SetException(ex);
  846. }
  847. }
  848. else
  849. {
  850. result.SetCanceled();
  851. }
  852. return result.Task;
  853. }
  854. #endregion
  855. }
  856. /// <summary>
  857. /// Represents the method that will handle the <see cref="MySqlConnection.InfoMessage"/> event of a
  858. /// <see cref="MySqlConnection"/>.
  859. /// </summary>
  860. internal delegate void MySqlInfoMessageEventHandler(object sender, MySqlInfoMessageEventArgs args);
  861. /// <summary>
  862. /// Provides data for the InfoMessage event. This class cannot be inherited.
  863. /// </summary>
  864. internal class MySqlInfoMessageEventArgs : EventArgs
  865. {
  866. /// <summary>
  867. ///
  868. /// </summary>
  869. public MySqlError[] errors;
  870. }
  871. /// <summary>
  872. /// IDisposable wrapper around SetCommandTimeout and ClearCommandTimeout
  873. /// functionality
  874. /// </summary>
  875. internal class CommandTimer : IDisposable
  876. {
  877. bool timeoutSet;
  878. MySqlConnection connection;
  879. public CommandTimer(MySqlConnection connection, int timeout)
  880. {
  881. this.connection = connection;
  882. if (connection != null)
  883. {
  884. timeoutSet = connection.SetCommandTimeout(timeout);
  885. }
  886. }
  887. #region IDisposable Members
  888. public void Dispose()
  889. {
  890. if (timeoutSet)
  891. {
  892. timeoutSet = false;
  893. connection.ClearCommandTimeout();
  894. connection = null;
  895. }
  896. }
  897. #endregion
  898. }
  899. }
  900. #endif