context: // This program is free software; you can redistribute it and/or modify
  // it under the terms of the GNU Lesser General Public License as published
  // by the Free Software Foundation; version 3 of the License.
  //
  // This program is distributed in the hope that it will be useful, but
  // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
  // for more details.
  //
  // You should have received a copy of the GNU Lesser General Public License along
  // with this program; if not, write to the Free Software Foundation, Inc.,
  // 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
  
  using System;
  using System.Data;
  using System.Data.Common;
  using IsolationLevel = System.Data.IsolationLevel;
  using MariaDB.Data.Common;
  
  
  namespace MariaDB.Data.MySqlClient
  {
      /// <include file='docs/MySqlConnection.xml' path='docs/ClassSummary/*'/>    
      public sealed class MySqlConnection : DbConnection
      {
          internal ConnectionState connectionState;
          internal Driver driver;
          private MySqlConnectionStringBuilder settings;
          private bool hasBeenOpen;
          private bool isInUse;
          private bool isKillQueryConnection;
          private string database;
          private int commandTimeout;
  
          /// <include file='docs/MySqlConnection.xml' path='docs/InfoMessage/*'/>
          public event MySqlInfoMessageEventHandler InfoMessage;
  
          private static Cache<string, MySqlConnectionStringBuilder> connectionStringCache =
             new Cache<string, MySqlConnectionStringBuilder>(0, 25);
  
          /// <include file='docs/MySqlConnection.xml' path='docs/DefaultCtor/*'/>
          public MySqlConnection()
          {
              //TODO: add event data to StateChange docs
              settings = new MySqlConnectionStringBuilder();
              database = String.Empty;
          }
  
          /// <include file='docs/MySqlConnection.xml' path='docs/Ctor1/*'/>
          public MySqlConnection(string connectionString)
              : this()
          {
              ConnectionString = connectionString;
          }
  
          internal MySqlConnectionStringBuilder Settings
          {
              get { return settings; }
          }
  
          internal MySqlDataReader Reader
          {
              get
              {
                  if (driver == null)
                      return null;
                  return driver.reader;
              }
              set
              {
                  driver.reader = value;
                  isInUse = driver.reader != null;
              }
          }
  
          internal void OnInfoMessage(MySqlInfoMessageEventArgs args)
          {
              if (InfoMessage != null)
              {
                  InfoMessage(this, args);
              }
          }
  
          internal bool SoftClosed
          {
              get
              {
                  return (State == ConnectionState.Closed) && driver != null;
              }
          }
  
          internal bool IsInUse
          {
              get { return isInUse; }
              set { isInUse = value; }
          }
  
          /// <summary>
          /// Returns the id of the server thread this connection is executing on
          /// </summary>
          public int ServerThread
          {
              get { return driver.ThreadID; }
          }
  
          /// <summary>
          /// Gets the name of the MySQL server to which to connect.
          /// </summary>
          public override string DataSource
          {
              get { return settings.Server; }
          }
  
          /// <include file='docs/MySqlConnection.xml' path='docs/ConnectionTimeout/*'/>
          public override int ConnectionTimeout
          {
              get { return (int)settings.ConnectionTimeout; }
          }
  
          /// <include file='docs/MySqlConnection.xml' path='docs/Database/*'/>
          public override string Database
          {
              get { return database; }
          }
  
          /// <summary>
          /// Indicates if this connection should use compression when communicating with the server.
          /// </summary>
          public bool UseCompression
          {
              get { return settings.UseCompression; }
          }
  
          /// <include file='docs/MySqlConnection.xml' path='docs/State/*'/>
          public override ConnectionState State
          {
              get { return connectionState; }
          }
  
          /// <include file='docs/MySqlConnection.xml' path='docs/ServerVersion/*'/>
          public override string ServerVersion
          {
              get { return driver.Version.ToString(); }
          }
  
          /// <include file='docs/MySqlConnection.xml' path='docs/ConnectionString/*'/>
          public override string ConnectionString
          {
              get
              {
                  // Always return exactly what the user set.
                  // Security-sensitive information may be removed.
                  return settings.GetConnectionString(!hasBeenOpen || settings.PersistSecurityInfo);
              }
              set
              {
                  if (State != ConnectionState.Closed)
                      throw new MySqlException(
                          "Not allowed to change the 'ConnectionString' property while the connection (state=" + State +
                          ").");
  
                  MySqlConnectionStringBuilder newSettings;
                  lock (connectionStringCache)
                  {
                      if (value == null)
                          newSettings = new MySqlConnectionStringBuilder();
                      else
                      {
                          newSettings = (MySqlConnectionStringBuilder)connectionStringCache[value];
                          if (null == newSettings)
                          {
                              newSettings = new MySqlConnectionStringBuilder(value);
                              connectionStringCache.Add(value, newSettings);
                          }
                      }
                  }
  
                  settings = newSettings;
  
                  if (settings.Database != null && settings.Database.Length > 0)
                      this.database = settings.Database;
  
                  if (driver != null)
                      driver.Settings = newSettings;
              }
          }
  
          /// <include file='docs/MySqlConnection.xml' path='docs/BeginTransaction/*'/>
          public new MySqlTransaction BeginTransaction()
          {
              return BeginTransaction(IsolationLevel.RepeatableRead);
          }
  
          /// <include file='docs/MySqlConnection.xml' path='docs/BeginTransaction1/*'/>
          public new MySqlTransaction BeginTransaction(IsolationLevel iso)
          {
              //TODO: check note in help
              if (State != ConnectionState.Open)
                  throw new InvalidOperationException(ResourceStrings.ConnectionNotOpen);
  
              // First check to see if we are in a current transaction
              if (driver.HasStatus(ServerStatusFlags.InTransaction))
                  throw new InvalidOperationException(ResourceStrings.NoNestedTransactions);
  
              MySqlTransaction t = new MySqlTransaction(this, iso);
  
              MySqlCommand cmd = new MySqlCommand("", this);
  
              cmd.CommandText = "SET SESSION TRANSACTION ISOLATION LEVEL ";
              switch (iso)
              {
                  case IsolationLevel.ReadCommitted:
                      cmd.CommandText += "READ COMMITTED";
                      break;
  
                  case IsolationLevel.ReadUncommitted:
                      cmd.CommandText += "READ UNCOMMITTED";
                      break;
  
                  case IsolationLevel.RepeatableRead:
                      cmd.CommandText += "REPEATABLE READ";
                      break;
  
                  case IsolationLevel.Serializable:
                      cmd.CommandText += "SERIALIZABLE";
                      break;
  
                  case IsolationLevel.Chaos:
                      throw new NotSupportedException(ResourceStrings.ChaosNotSupported);
                  case IsolationLevel.Snapshot:
                      throw new NotSupportedException(ResourceStrings.SnapshotNotSupported);
              }
  
              cmd.ExecuteNonQuery();
  
              cmd.CommandText = "BEGIN";
              cmd.ExecuteNonQuery();
  
              return t;
          }
  
          /// <include file='docs/MySqlConnection.xml' path='docs/ChangeDatabase/*'/>
          public override void ChangeDatabase(string databaseName)
          {
              if (databaseName == null || databaseName.Trim().Length == 0)
                  throw new ArgumentException(ResourceStrings.ParameterIsInvalid, "databaseName");
  
              if (State != ConnectionState.Open)
                  throw new InvalidOperationException(ResourceStrings.ConnectionNotOpen);
  
              // This lock  prevents promotable transaction rollback to run
              // in parallel
              lock (driver)
              {
                  // We use default command timeout for SetDatabase
                  using (new CommandTimer(this, (int)Settings.DefaultCommandTimeout))
                  {
                      driver.SetDatabase(databaseName);
                  }
              }
              this.database = databaseName;
          }
  
          internal void SetState(ConnectionState newConnectionState, bool broadcast)
          {
              if (newConnectionState == connectionState && !broadcast)
                  return;
              ConnectionState oldConnectionState = connectionState;
              connectionState = newConnectionState;
              if (broadcast)
                  OnStateChange(new StateChangeEventArgs(oldConnectionState, connectionState));
          }
  
          /// <summary>
          /// Ping
          /// </summary>
          /// <returns></returns>
          public bool Ping()
          {
              if (Reader != null)
                  throw new MySqlException(ResourceStrings.DataReaderOpen);
              if (driver != null && driver.Ping())
                  return true;
              driver = null;
              SetState(ConnectionState.Closed, true);
              return false;
          }
  
          /// <include file='docs/MySqlConnection.xml' path='docs/Open/*'/>
          public override void Open()
          {
              if (State == ConnectionState.Open)
                  throw new InvalidOperationException(ResourceStrings.ConnectionAlreadyOpen);
              SetState(ConnectionState.Connecting, true);
              try
              {
                  if (settings.Pooling)
                  {
                      MySqlPool pool = MySqlPoolManager.GetPool(settings);
                      if (driver == null || !driver.IsOpen)
                          driver = pool.GetConnection();
                  }
                  else
                  {
                      if (driver == null || !driver.IsOpen)
                          driver = Driver.Create(settings);
                  }
              }
              catch (Exception)
              {
                  SetState(ConnectionState.Closed, true);
                  throw;
              }
  
              SetState(ConnectionState.Open, false);
              driver.Configure(this);
              if (settings.Database != null && settings.Database != String.Empty)
                  ChangeDatabase(settings.Database);
              hasBeenOpen = true;
              SetState(ConnectionState.Open, true);
          }
  
          /// <include file='docs/MySqlConnection.xml' path='docs/CreateCommand/*'/>
          public new MySqlCommand CreateCommand()
          {
              // Return a new instance of a command object.
              MySqlCommand c = new MySqlCommand();
              c.Connection = this;
              return c;
          }
  
          /// <summary>
          /// Creates a new MySqlConnection object with the exact same ConnectionString value
          /// </summary>
          /// <returns>A cloned MySqlConnection object</returns>
          public MySqlConnection Clone()
          {
              MySqlConnection clone = new MySqlConnection();
              string connectionString = settings.ConnectionString;
              if (connectionString != null)
                  clone.ConnectionString = connectionString;
              return clone;
          }
  
          protected override void Dispose(bool disposing)
          {
              if (State == ConnectionState.Open)
                  Close();
              base.Dispose(disposing);
          }
  
          protected override DbTransaction BeginDbTransaction(IsolationLevel isolationLevel)
          {
              if (isolationLevel == IsolationLevel.Unspecified)
                  return BeginTransaction();
              return BeginTransaction(isolationLevel);
          }
  
          protected override DbCommand CreateDbCommand()
          {
              return CreateCommand();
          }
  
          internal void Abort()
          {
              try
              {
                  driver.Close();
              }
              catch { }
              finally
              {
                  this.isInUse = false;
              }
              SetState(ConnectionState.Closed, true);
          }
  
          internal void CloseFully()
          {
  
gt:             if (settings.Pooling && driver.IsOpen)