Duplex client has its own listener loop, so special care on reply is needed.
[mono.git] / mcs / class / System.Data / System.Data.SqlClient / SqlConnection.cs
index 988b0ce99bed9b92b99312bbcd5a1193820437f2..ef5a4fc18b398c1719c69e779ff70f75779e9838 100644 (file)
@@ -45,14 +45,20 @@ using System.Collections.Specialized;
 using System.ComponentModel;
 using System.Data;
 using System.Data.Common;
+#if !MOBILE
 using System.EnterpriseServices;
+#endif
 using System.Globalization;
 using System.Net;
 using System.Net.Sockets;
 using System.Text;
 using System.Xml;
+#if NET_2_0
+using System.Collections.Generic;
+#endif
 
-namespace System.Data.SqlClient {
+namespace System.Data.SqlClient
+{
        [DefaultEvent ("InfoMessage")]
 #if NET_2_0
        public sealed class SqlConnection : DbConnection, IDbConnection, ICloneable
@@ -61,24 +67,37 @@ namespace System.Data.SqlClient {
 #endif // NET_2_0
        {
                #region Fields
-               bool disposed = false;
+
+               bool disposed;
 
                // The set of SQL connection pools
-               static TdsConnectionPoolManager sqlConnectionPools = new TdsConnectionPoolManager (TdsVersion.tds70);
+               static TdsConnectionPoolManager sqlConnectionPools = new TdsConnectionPoolManager (TdsVersion.tds80);
+#if NET_2_0
+               const int DEFAULT_PACKETSIZE = 8000;
+               const int MAX_PACKETSIZE = 32768;
+#else
+               const int DEFAULT_PACKETSIZE = 8192;
+               const int MAX_PACKETSIZE = 32767;
+#endif
+               const int MIN_PACKETSIZE = 512;
+               const int DEFAULT_CONNECTIONTIMEOUT = 15;
+               const int DEFAULT_MAXPOOLSIZE = 100;
+               const int MIN_MAXPOOLSIZE = 1;
+               const int DEFAULT_MINPOOLSIZE = 0;
+               const int DEFAULT_PORT = 1433;
 
                // The current connection pool
                TdsConnectionPool pool;
 
                // The connection string that identifies this connection
-               string connectionString = null;
+               string connectionString;
 
                // The transaction object for the current transaction
-               SqlTransaction transaction = null;
+               SqlTransaction transaction;
 
                // Connection parameters
                
-               TdsConnectionParameters parms = new TdsConnectionParameters ();
-               NameValueCollection connStringParameters = null;
+               TdsConnectionParameters parms;
                bool connectionReset;
                bool pooling;
                string dataSource;
@@ -86,39 +105,30 @@ namespace System.Data.SqlClient {
                int minPoolSize;
                int maxPoolSize;
                int packetSize;
-               int port = 1433;
+               int port;
                bool fireInfoMessageEventOnUserErrors;
                bool statisticsEnabled;
-
+               
                // The current state
                ConnectionState state = ConnectionState.Closed;
 
-               SqlDataReader dataReader = null;
-               XmlReader xmlReader = null;
+               SqlDataReader dataReader;
+               XmlReader xmlReader;
 
                // The TDS object
-               ITds tds;
+               Tds tds;
 
                #endregion // Fields
 
                #region Constructors
 
-               public SqlConnection () 
-                       : this (String.Empty)
+               public SqlConnection () : this (null)
                {
                }
        
                public SqlConnection (string connectionString)
                {
-                       Init (connectionString);
-               }
-
-               private void Init (string connectionString)
-               {
-                       connectionTimeout       = 15; // default timeout
-                       dataSource              = ""; // default datasource
-                       packetSize              = 8192; // default packetsize
-                       ConnectionString        = connectionString;
+                       ConnectionString = connectionString;
                }
 
                #endregion // Constructors
@@ -130,23 +140,23 @@ namespace System.Data.SqlClient {
 #endif
                [DefaultValue ("")]
                [EditorAttribute ("Microsoft.VSDesigner.Data.SQL.Design.SqlConnectionStringEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]
-#if NET_2_0
-               [SettingsBindableAttribute (true)]
-#else
                [RecommendedAsConfigurable (true)]
-#endif
                [RefreshProperties (RefreshProperties.All)]
-               [MonoTODO("persist security info, encrypt, enlist keyword not implemented")]
-               public 
+               public
 #if NET_2_0
                override
 #endif // NET_2_0
                string ConnectionString {
-                       get { return connectionString; }
+                       get {
+                               if (connectionString == null)
+                                       return string.Empty;
+                               return connectionString;
+                       }
+                       [MonoTODO("persist security info, encrypt, enlist keyword not implemented")]
                        set {
                                if (state == ConnectionState.Open)
                                        throw new InvalidOperationException ("Not Allowed to change ConnectionString property while Connection state is OPEN");
-                               SetConnectionString (value); 
+                               SetConnectionString (value);
                        }
                }
        
@@ -154,7 +164,7 @@ namespace System.Data.SqlClient {
                [DataSysDescription ("Current connection timeout value, 'Connect Timeout=X' in the ConnectionString.")] 
 #endif
                [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
-               public 
+               public
 #if NET_2_0
                override
 #endif // NET_2_0
@@ -166,7 +176,7 @@ namespace System.Data.SqlClient {
                [DataSysDescription ("Current SQL Server database, 'Initial Catalog=X' in the connection string.")]
 #endif
                [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
-               public 
+               public
 #if NET_2_0
                override
 #endif // NET_2_0
@@ -189,7 +199,7 @@ namespace System.Data.SqlClient {
                [Browsable(true)]
 #endif
                [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
-               public 
+               public
 #if NET_2_0
                override
 #endif // NET_2_0
@@ -203,9 +213,9 @@ namespace System.Data.SqlClient {
                [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
                public int PacketSize {
                        get {
-                               if (State == ConnectionState.Open) 
-                                       return ((Tds)tds).PacketSize ;
-                               return packetSize; 
+                               if (State == ConnectionState.Open)
+                                       return ((Tds) tds).PacketSize;
+                               return packetSize;
                        }
                }
 
@@ -214,14 +224,14 @@ namespace System.Data.SqlClient {
                [DataSysDescription ("Version of the SQL Server accessed by the SqlConnection.")]
 #endif
                [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
-               public 
+               public
 #if NET_2_0
                override
 #endif // NET_2_0
                string ServerVersion {
                        get {
                                if (state == ConnectionState.Closed)
-                                       throw new InvalidOperationException ("Invalid Operation.The Connection is Closed");
+                                       throw ExceptionHelper.ConnectionClosed ();
                                else
                                        return tds.ServerVersion; 
                        }
@@ -232,7 +242,7 @@ namespace System.Data.SqlClient {
                [DataSysDescription ("The ConnectionState indicating whether the connection is open or closed.")]
 #endif
                [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
-               public 
+               public
 #if NET_2_0
                override
 #endif // NET_2_0
@@ -240,7 +250,7 @@ namespace System.Data.SqlClient {
                        get { return state; }
                }
 
-               internal ITds Tds {
+               internal Tds Tds {
                        get { return tds; }
                }
 
@@ -294,6 +304,15 @@ namespace System.Data.SqlClient {
 
                private void ErrorHandler (object sender, TdsInternalErrorMessageEventArgs e)
                {
+                       try {
+                               if (!tds.IsConnected)
+                                       Close ();
+                       } catch {
+                               try {
+                                       Close ();
+                               } catch {
+                               }
+                       }
                        throw new SqlException (e.Class, e.LineNumber, e.Message, e.Number, e.Procedure, e.Server, "Mono SqlClient Data Provider", e.State);
                }
 
@@ -306,19 +325,6 @@ namespace System.Data.SqlClient {
 
                #region Methods
 
-               internal string GetConnStringKeyValue (params string [] keys)
-               {
-                       if (connStringParameters == null || connStringParameters.Count == 0)
-                               return "";
-                       foreach (string key in keys) {
-                               string value = connStringParameters [key];
-                               if (value != null)
-                                       return value;
-                       }
-
-                       return "";
-               }
-
                public new SqlTransaction BeginTransaction ()
                {
                        return BeginTransaction (IsolationLevel.ReadCommitted, String.Empty);
@@ -337,18 +343,12 @@ namespace System.Data.SqlClient {
                public SqlTransaction BeginTransaction (IsolationLevel iso, string transactionName)
                {
                        if (state == ConnectionState.Closed)
-                               throw new InvalidOperationException ("The connection is not open.");
+                               throw ExceptionHelper.ConnectionClosed ();
                        if (transaction != null)
                                throw new InvalidOperationException ("SqlConnection does not support parallel transactions.");
 
-                       if (iso == IsolationLevel.Chaos)
-                               throw new ArgumentException ("Invalid IsolationLevel parameter: must be ReadCommitted, ReadUncommitted, RepeatableRead, or Serializable.");
-
                        string isolevel = String.Empty;
                        switch (iso) {
-                       case IsolationLevel.ReadCommitted:
-                               isolevel = "READ COMMITTED";
-                               break;
                        case IsolationLevel.ReadUncommitted:
                                isolevel = "READ UNCOMMITTED";
                                break;
@@ -358,6 +358,34 @@ namespace System.Data.SqlClient {
                        case IsolationLevel.Serializable:
                                isolevel = "SERIALIZABLE";
                                break;
+                       case IsolationLevel.ReadCommitted:
+                               isolevel = "READ COMMITTED";
+                               break;
+#if NET_2_0
+                       case IsolationLevel.Snapshot:
+                               isolevel = "SNAPSHOT";
+                               break;
+                       case IsolationLevel.Unspecified:
+                               iso = IsolationLevel.ReadCommitted;
+                               isolevel = "READ COMMITTED";
+                               break;
+                       case IsolationLevel.Chaos:
+                               throw new ArgumentOutOfRangeException ("IsolationLevel",
+                                       string.Format (CultureInfo.CurrentCulture,
+                                               "The IsolationLevel enumeration " +
+                                               "value, {0}, is not supported by " +
+                                               "the .Net Framework SqlClient " +
+                                               "Data Provider.", (int) iso));
+#endif
+                       default:
+#if NET_2_0
+                               throw new ArgumentOutOfRangeException ("IsolationLevel",
+                                       string.Format (CultureInfo.CurrentCulture,
+                                               "The IsolationLevel enumeration value, {0}, is invalid.",
+                                               (int) iso));
+#else
+                               throw new ArgumentException ("Invalid IsolationLevel parameter: must be ReadCommitted, ReadUncommitted, RepeatableRead, or Serializable.");
+#endif
                        }
 
                        tds.Execute (String.Format ("SET TRANSACTION ISOLATION LEVEL {0};BEGIN TRANSACTION {1}", isolevel, transactionName));
@@ -366,11 +394,11 @@ namespace System.Data.SqlClient {
                        return transaction;
                }
 
-               public 
+               public
 #if NET_2_0
                override
 #endif // NET_2_0
-        void ChangeDatabase (string database) 
+               void ChangeDatabase (string database)
                {
                        if (!IsValidDatabaseName (database))
                                throw new ArgumentException (String.Format ("The database name {0} is not valid.", database));
@@ -381,16 +409,19 @@ namespace System.Data.SqlClient {
 
                private void ChangeState (ConnectionState currentState)
                {
+                       if (currentState == state)
+                               return;
+
                        ConnectionState originalState = state;
                        state = currentState;
                        OnStateChange (CreateStateChangeEvent (originalState, currentState));
                }
 
-               public 
+               public
 #if NET_2_0
                override
 #endif // NET_2_0
-               void Close () 
+               void Close ()
                {
                        if (transaction != null && transaction.IsOpen)
                                transaction.Rollback ();
@@ -401,10 +432,16 @@ namespace System.Data.SqlClient {
                                xmlReader = null;
                        }
 
-                       if (pooling) {
-                               if(pool != null) pool.ReleaseConnection (tds);
-                       }else
-                               if(tds != null) tds.Disconnect ();
+                       if (tds != null && tds.IsConnected) {
+                               if (pooling && tds.Pooling) {
+                                       if (pool != null) {
+                                               pool.ReleaseConnection (tds);
+                                               pool = null;
+                                       }
+                               } else {
+                                       tds.Disconnect ();
+                               }
+                       }
 
                        if (tds != null) {
                                tds.TdsErrorMessage -= new TdsInternalErrorMessageEventHandler (ErrorHandler);
@@ -414,7 +451,7 @@ namespace System.Data.SqlClient {
                        ChangeState (ConnectionState.Closed);
                }
 
-               public new SqlCommand CreateCommand () 
+               public new SqlCommand CreateCommand ()
                {
                        SqlCommand command = new SqlCommand ();
                        command.Connection = this;
@@ -431,17 +468,13 @@ namespace System.Data.SqlClient {
                        return new StateChangeEventArgs (originalState, currentState);
                }
 
-               protected override void Dispose (bool disposing) 
+               protected override void Dispose (bool disposing)
                {
-                       if (disposed)
-                               return;
-
                        try {
-                               if (disposing) {
+                               if (disposing && !disposed) {
                                        if (State == ConnectionState.Open) 
                                                Close ();
-                                       ConnectionString = "";
-                                       SetDefaultConnectionParameters (this.connStringParameters); 
+                                       ConnectionString = null;
                                }
                        } finally {
                                disposed = true;
@@ -449,11 +482,13 @@ namespace System.Data.SqlClient {
                        }
                }
 
+#if !MOBILE
                [MonoTODO ("Not sure what this means at present.")]
                public void EnlistDistributedTransaction (ITransaction transaction)
                {
                        throw new NotImplementedException ();
                }
+#endif
 
                object ICloneable.Clone ()
                {
@@ -461,9 +496,9 @@ namespace System.Data.SqlClient {
                }
 
 #if NET_2_0
-               protected override DbTransaction BeginDbTransaction (IsolationLevel level)
+               protected override DbTransaction BeginDbTransaction (IsolationLevel isolationLevel)
                {
-                       return (DbTransaction)BeginTransaction (level);
+                       return BeginTransaction (isolationLevel);
                }
 
                protected override DbCommand CreateDbCommand ()
@@ -487,13 +522,13 @@ namespace System.Data.SqlClient {
                }
 #endif
 
-               public 
+               public
 #if NET_2_0
                override
 #endif // NET_2_0
-               void Open () 
+               void Open ()
                {
-                       string serverName = "";
+                       string serverName = string.Empty;
                        if (state == ConnectionState.Open)
                                throw new InvalidOperationException ("The Connection is already Open (State=Open)");
 
@@ -504,7 +539,8 @@ namespace System.Data.SqlClient {
                                if (!pooling) {
                                        if(!ParseDataSource (dataSource, out port, out serverName))
                                                throw new SqlException(20, 0, "SQL Server does not exist or access denied.",  17, "ConnectionOpen (Connect()).", dataSource, parms.ApplicationName, 0);
-                                       tds = new Tds70 (serverName, port, PacketSize, ConnectionTimeout);
+                                       tds = new Tds80 (serverName, port, PacketSize, ConnectionTimeout);
+                                       tds.Pooling = false;
                                }
                                else {
                                        if(!ParseDataSource (dataSource, out port, out serverName))
@@ -516,7 +552,7 @@ namespace System.Data.SqlClient {
                                }
                        } catch (TdsTimeoutException e) {
                                throw SqlException.FromTdsInternalException ((TdsInternalException) e);
-                       }catch (TdsInternalException e) {
+                       } catch (TdsInternalException e) {
                                throw SqlException.FromTdsInternalException (e);
                        }
 
@@ -526,114 +562,105 @@ namespace System.Data.SqlClient {
                        if (!tds.IsConnected) {
                                try {
                                        tds.Connect (parms);
-                               }
-                               catch {
+                               } catch {
                                        if (pooling)
                                                pool.ReleaseConnection (tds);
                                        throw;
                                }
-                       } else if (connectionReset) {
-                               tds.Reset ();
                        }
 
                        disposed = false; // reset this, so using () would call Close ().
                        ChangeState (ConnectionState.Open);
                }
 
-               private bool ParseDataSource (string theDataSource, out int thePort, out string theServerName) 
+               private bool ParseDataSource (string theDataSource, out int thePort, out string theServerName)
                {
-                       theServerName = "";
-                       string theInstanceName = "";
+                       theServerName = string.Empty;
+                       string theInstanceName = string.Empty;
        
                        if (theDataSource == null)
                                throw new ArgumentException("Format of initialization string does not conform to specifications");
 
-                       thePort = 1433; // default TCP port for SQL Server
+                       thePort = DEFAULT_PORT; // default TCP port for SQL Server
                        bool success = true;
 
                        int idx = 0;
-                       if ((idx = theDataSource.IndexOf (",")) > -1) {
+                       if ((idx = theDataSource.IndexOf (',')) > -1) {
                                theServerName = theDataSource.Substring (0, idx);
                                string p = theDataSource.Substring (idx + 1);
                                thePort = Int32.Parse (p);
-                       }
-                       else if ((idx = theDataSource.IndexOf ("\\")) > -1) {
+                       } else if ((idx = theDataSource.IndexOf ('\\')) > -1) {
                                theServerName = theDataSource.Substring (0, idx);
                                theInstanceName = theDataSource.Substring (idx + 1);
+
                                // do port discovery via UDP port 1434
                                port = DiscoverTcpPortViaSqlMonitor (theServerName, theInstanceName);
                                if (port == -1)
                                        success = false;
-                       }
-                       else if (theDataSource == "" || theDataSource == "(local)")
-                               theServerName = "localhost";
-                       else
+                       } else
                                theServerName = theDataSource;
 
-                       if ((idx = theServerName.IndexOf ("tcp:")) > -1) {
+                       if (theServerName.Length == 0 || theServerName == "(local)" || theServerName == ".")
+                               theServerName = "localhost";
+
+                       if ((idx = theServerName.IndexOf ("tcp:")) > -1)
                                theServerName = theServerName.Substring (idx + 4);
-                       }
 
                        return success;
                }
 
                private bool ConvertIntegratedSecurity (string value)
                {
-                       if (value.ToUpper() == "SSPI") 
-                       {
+                       if (value.ToUpper() == "SSPI")
                                return true;
-                       }
 
-                       return ConvertToBoolean("integrated security", value);
+                       return ConvertToBoolean ("integrated security", value, false);
                }
 
-               private bool ConvertToBoolean(string key, string value)
+               private bool ConvertToBoolean (string key, string value, bool defaultValue)
                {
-                       string upperValue = value.ToUpper();
+                       if (value.Length == 0)
+                               return defaultValue;
 
-                       if (upperValue == "TRUE" ||upperValue == "YES")
-                       {
+                       string upperValue = value.ToUpper ();
+
+                       if (upperValue == "TRUE" || upperValue == "YES")
                                return true;
-                       } 
                        else if (upperValue == "FALSE" || upperValue == "NO")
-                       {
                                return false;
-                       }
 
-                       throw new ArgumentException(string.Format(CultureInfo.InvariantCulture,
+                       throw new ArgumentException (string.Format (CultureInfo.InvariantCulture,
                                "Invalid value \"{0}\" for key '{1}'.", value, key));
                }
 
-               private int ConvertToInt32(string key, string value)
+               private int ConvertToInt32 (string key, string value, int defaultValue)
                {
-                       try
-                       {
-                               return int.Parse(value);
-                       }
-                       catch (Exception ex)
-                       {
-                               throw new ArgumentException(string.Format(CultureInfo.InvariantCulture,
-                                       "Invalid value \"{0}\" for key '{1}'.", value, key));
+                       if (value.Length == 0)
+                               return defaultValue;
+
+                       try {
+                               return int.Parse (value);
+                       } catch (Exception ex) {
+                               throw new ArgumentException (string.Format (CultureInfo.InvariantCulture,
+                                       "Invalid value \"{0}\" for key '{1}'.", value, key), ex);
                        }
                }
 
-               private int DiscoverTcpPortViaSqlMonitor(string ServerName, string InstanceName) 
+               private int DiscoverTcpPortViaSqlMonitor (string ServerName, string InstanceName) 
                {
                        SqlMonitorSocket msock;
                        msock = new SqlMonitorSocket (ServerName, InstanceName);
-                       int SqlServerPort = msock.DiscoverTcpPort ();
+                       int SqlServerPort = msock.DiscoverTcpPort (ConnectionTimeout);
                        msock = null;
                        return SqlServerPort;
                }
        
                void SetConnectionString (string connectionString)
                {
-                       NameValueCollection parameters = new NameValueCollection ();
-                       SetDefaultConnectionParameters (parameters);
+                       SetDefaultConnectionParameters ();
 
                        if ((connectionString == null) || (connectionString.Trim().Length == 0)) {
                                this.connectionString = connectionString;
-                               this.connStringParameters = parameters;
                                return;
                        }
 
@@ -682,8 +709,7 @@ namespace System.Data.SqlClient {
                                        else {
                                                if (name != String.Empty && name != null) {
                                                        value = sb.ToString ();
-                                                       SetProperties (name.ToUpper ().Trim() , value);
-                                                       parameters [name.ToUpper ().Trim ()] = value.Trim ();
+                                                       SetProperties (name.ToLower ().Trim() , value);
                                                }
                                                else if (sb.Length != 0)
                                                        throw new ArgumentException ("Format of initialization string does not conform to specifications");
@@ -719,169 +745,168 @@ namespace System.Data.SqlClient {
                                }
                        }
 
+                       if (minPoolSize > maxPoolSize)
+                               throw new ArgumentException ("Invalid value for "
+                                       + "'min pool size' or 'max pool size'; "
+                                       + "'min pool size' must not be greater "
+                                       + "than 'max pool size'.");
+
                        connectionString = connectionString.Substring (0 , connectionString.Length-1);
                        this.connectionString = connectionString;
-                       this.connStringParameters = parameters;
                }
 
-               void SetDefaultConnectionParameters (NameValueCollection parameters)
+               void SetDefaultConnectionParameters ()
                {
-                       parms.Reset ();
-                       dataSource = "";
-                       connectionTimeout= 15;
+                       if (parms == null)
+                               parms = new TdsConnectionParameters ();
+                       else
+                               parms.Reset ();
+                       dataSource = string.Empty;
+                       connectionTimeout = DEFAULT_CONNECTIONTIMEOUT;
                        connectionReset = true;
                        pooling = true;
-                       maxPoolSize = 100; 
-                       minPoolSize = 0;
-                       packetSize = 8192; 
-                       
-                       parameters["APPLICATION NAME"] = "Mono SqlClient Data Provider";
-                       parameters["CONNECT TIMEOUT"] = "15";
-                       parameters["CONNECTION LIFETIME"] = "0";
-                       parameters["CONNECTION RESET"] = "true";
-                       parameters["ENLIST"] = "true";
-                       parameters["INTEGRATED SECURITY"] = "false";
-                       parameters["INITIAL CATALOG"] = "";
-                       parameters["MAX POOL SIZE"] = "100";
-                       parameters["MIN POOL SIZE"] = "0";
-                       parameters["NETWORK LIBRARY"] = "dbmssocn";
-                       parameters["PACKET SIZE"] = "8192";
-                       parameters["PERSIST SECURITY INFO"] = "false";
-                       parameters["POOLING"] = "true";
-                       parameters["WORKSTATION ID"] = Dns.GetHostName();
+                       maxPoolSize = DEFAULT_MAXPOOLSIZE;
+                       minPoolSize = DEFAULT_MINPOOLSIZE;
+                       packetSize = DEFAULT_PACKETSIZE;
+                       port = DEFAULT_PORT;
  #if NET_2_0
                        async = false;
-                       parameters ["ASYNCHRONOUS PROCESSING"] = "false";
  #endif
                }
-               
+
                private void SetProperties (string name , string value)
                {
-
-                       switch (name) 
-                       {
-                       case "APP" :
-                       case "APPLICATION NAME" :
+                       switch (name) {
+                       case "app" :
+                       case "application name" :
                                parms.ApplicationName = value;
                                break;
-                       case "ATTACHDBFILENAME" :
-                       case "EXTENDED PROPERTIES" :
-                       case "INITIAL FILE NAME" :
+                       case "attachdbfilename" :
+                       case "extended properties" :
+                       case "initial file name" :
                                parms.AttachDBFileName = value;
                                break;
-                       case "TIMEOUT" :
-                       case "CONNECT TIMEOUT" :
-                       case "CONNECTION TIMEOUT" :
-                               int tmpTimeout = ConvertToInt32 ("connection timeout", value);
+                       case "timeout" :
+                       case "connect timeout" :
+                       case "connection timeout" :
+                               int tmpTimeout = ConvertToInt32 ("connect timeout", value,
+                                       DEFAULT_CONNECTIONTIMEOUT);
                                if (tmpTimeout < 0)
-                                       throw new ArgumentException ("Invalid CONNECTION TIMEOUT .. Must be an integer >=0 ");
+                                       throw new ArgumentException ("Invalid 'connect timeout'. Must be an integer >=0 ");
                                else 
                                        connectionTimeout = tmpTimeout;
                                break;
-                       case "CONNECTION LIFETIME" :
+                       case "connection lifetime" :
                                break;
-                       case "CONNECTION RESET" :
-                               connectionReset = ConvertToBoolean ("connection reset", value);
+                       case "connection reset" :
+                               connectionReset = ConvertToBoolean ("connection reset", value, true);
                                break;
-                       case "LANGUAGE" :
-                       case "CURRENT LANGUAGE" :
+                       case "language" :
+                       case "current language" :
                                parms.Language = value;
                                break;
-                       case "DATA SOURCE" :
-                       case "SERVER" :
-                       case "ADDRESS" :
-                       case "ADDR" :
-                       case "NETWORK ADDRESS" :
+                       case "data source" :
+                       case "server" :
+                       case "address" :
+                       case "addr" :
+                       case "network address" :
                                dataSource = value;
                                break;
-                       case "ENCRYPT":
-                               if (ConvertToBoolean("encrypt", value))
-                               {
+                       case "encrypt":
+                               if (ConvertToBoolean (name, value, false))
                                        throw new NotImplementedException("SSL encryption for"
                                                + " data sent between client and server is not"
                                                + " implemented.");
-                               }
                                break;
-                       case "ENLIST" :
-                               if (!ConvertToBoolean("enlist", value))
-                               {
+                       case "enlist" :
+                               if (!ConvertToBoolean (name, value, true))
                                        throw new NotImplementedException("Disabling the automatic"
                                                + " enlistment of connections in the thread's current"
                                                + " transaction context is not implemented.");
-                               }
                                break;
-                       case "INITIAL CATALOG" :
-                       case "DATABASE" :
+                       case "initial catalog" :
+                       case "database" :
                                parms.Database = value;
                                break;
-                       case "INTEGRATED SECURITY" :
-                       case "TRUSTED_CONNECTION" :
+                       case "integrated security" :
+                       case "trusted_connection" :
                                parms.DomainLogin = ConvertIntegratedSecurity(value);
                                break;
-                       case "MAX POOL SIZE" :
-                               int tmpMaxPoolSize = ConvertToInt32 ("max pool size" , value);
-                               if (tmpMaxPoolSize < 0)
-                                       throw new ArgumentException ("Invalid MAX POOL SIZE. Must be a intger >= 0");
+                       case "max pool size" :
+                               int tmpMaxPoolSize = ConvertToInt32 (name, value, DEFAULT_MAXPOOLSIZE);
+                               if (tmpMaxPoolSize < MIN_MAXPOOLSIZE)
+                                       throw new ArgumentException (string.Format (CultureInfo.InvariantCulture,
+                                               "Invalid '{0}'. The value must be greater than {1}.",
+                                               name, MIN_MAXPOOLSIZE));
                                else
-                                       maxPoolSize = tmpMaxPoolSize; 
+                                       maxPoolSize = tmpMaxPoolSize;
                                break;
-                       case "MIN POOL SIZE" :
-                               int tmpMinPoolSize = ConvertToInt32 ("min pool size" , value);
+                       case "min pool size" :
+                               int tmpMinPoolSize = ConvertToInt32 (name, value, DEFAULT_MINPOOLSIZE);
                                if (tmpMinPoolSize < 0)
-                                       throw new ArgumentException ("Invalid MIN POOL SIZE. Must be a intger >= 0");
+                                       throw new ArgumentException ("Invalid 'min pool size'. Must be a integer >= 0");
                                else
                                        minPoolSize = tmpMinPoolSize;
                                break;
-#if NET_2_0    
-                       case "MULTIPLEACTIVERESULTSETS":
+#if NET_2_0
+                       case "multipleactiveresultsets":
+                               // FIXME: not implemented
+                               ConvertToBoolean (name, value, false);
                                break;
-                       case "ASYNCHRONOUS PROCESSING" :
-                       case "ASYNC" :
-                               async = ConvertToBoolean (name, value);
+                       case "asynchronous processing" :
+                       case "async" :
+                               async = ConvertToBoolean (name, value, false);
                                break;
-#endif 
-                       case "NET" :
-                       case "NETWORK" :
-                       case "NETWORK LIBRARY" :
+#endif
+                       case "net" :
+                       case "network" :
+                       case "network library" :
                                if (!value.ToUpper ().Equals ("DBMSSOCN"))
                                        throw new ArgumentException ("Unsupported network library.");
                                break;
-                       case "PACKET SIZE" :
-                               int tmpPacketSize = ConvertToInt32 ("packet size", value);
-                               if (tmpPacketSize < 512 || tmpPacketSize > 32767)
-                                       throw new ArgumentException ("Invalid PACKET SIZE. The integer must be between 512 and 32767");
+                       case "packet size" :
+                               int tmpPacketSize = ConvertToInt32 (name, value, DEFAULT_PACKETSIZE);
+                               if (tmpPacketSize < MIN_PACKETSIZE || tmpPacketSize > MAX_PACKETSIZE)
+                                       throw new ArgumentException (string.Format (CultureInfo.InvariantCulture,
+                                               "Invalid 'Packet Size'. The value must be between {0} and {1}.",
+                                               MIN_PACKETSIZE, MAX_PACKETSIZE));
                                else
                                        packetSize = tmpPacketSize;
                                break;
-                       case "PASSWORD" :
-                       case "PWD" :
+                       case "password" :
+                       case "pwd" :
                                parms.Password = value;
                                break;
-                       case "PERSISTSECURITYINFO" :
-                       case "PERSIST SECURITY INFO" :
+                       case "persistsecurityinfo" :
+                       case "persist security info" :
                                // FIXME : not implemented
                                // throw new NotImplementedException ();
                                break;
-                       case "POOLING" :
-                               pooling = ConvertToBoolean("pooling", value);
+                       case "pooling" :
+                               pooling = ConvertToBoolean (name, value, true);
                                break;
-                       case "UID" :
-                       case "USER" :
-                       case "USER ID" :
+                       case "uid" :
+                       case "user" :
+                       case "user id" :
                                parms.User = value;
                                break;
-                       case "WSID" :
-                       case "WORKSTATION ID" :
+                       case "wsid" :
+                       case "workstation id" :
                                parms.Hostname = value;
                                break;
+#if NET_2_0
+                       case "user instance":
+                               userInstance = ConvertToBoolean (name, value, false);
+                               break;
+#endif
                        default :
-                               throw new ArgumentException("Keyword not supported :"+name);
+                               throw new ArgumentException("Keyword not supported : '" + name + "'.");
                        }
                }
 
                static bool IsValidDatabaseName (string database)
                {
-                       if ( database == null || database.Trim() == String.Empty || database.Length > 128)
+                       if ( database == null || database.Trim().Length == 0 || database.Length > 128)
                                return false ;
                        
                        if (database[0] == '"' && database[database.Length] == '"')
@@ -912,7 +937,7 @@ namespace System.Data.SqlClient {
                }
 #endif
 
-               private sealed class SqlMonitorSocket : UdpClient 
+               private sealed class SqlMonitorSocket : UdpClient
                {
                        // UDP port that the SQL Monitor listens
                        private static readonly int SqlMonitorUdpPort = 1434;
@@ -928,7 +953,7 @@ namespace System.Data.SqlClient {
                                instance = InstanceName;
                        }
 
-                       internal int DiscoverTcpPort (
+                       internal int DiscoverTcpPort (int timeoutSeconds)
                        {
                                int SqlServerTcpPort;
                                Client.Blocking = false;
@@ -938,13 +963,14 @@ namespace System.Data.SqlClient {
                                Byte[] rawrq = new Byte [instance.Length + 1];
                                rawrq[0] = 4;
                                enc.GetBytes (instance, 0, instance.Length, rawrq, 1);
-                               int bytes = Send (rawrq, rawrq.Length);
+                               Send (rawrq, rawrq.Length);
 
                                if (!Active)
                                        return -1; // Error
                                
                                bool result;
-                               result = Client.Poll (100, SelectMode.SelectRead);
+                               long timeout = timeoutSeconds * 1000000;
+                               result = Client.Poll ((int)timeout, SelectMode.SelectRead);
                                if (result == false)
                                        return -1; // Error
 
@@ -966,8 +992,13 @@ namespace System.Data.SqlClient {
                                for (int i = 0; i < rawtokens.Length / 2 && i < 256; i++) {
                                        data [rawtokens [i * 2]] = rawtokens [ i * 2 + 1];
                                }
-                               if (!data.ContainsKey ("tcp")) 
-                                       throw new NotImplementedException ("Only TCP/IP is supported.");
+
+                               if (!data.ContainsKey ("tcp")) {
+                                       string msg = "Mono does not support names pipes or shared memory "
+                                               + "for connecting to SQL Server. Please enable the TCP/IP "
+                                               + "protocol.";
+                                       throw new NotImplementedException (msg);
+                               }
 
                                SqlServerTcpPort = int.Parse ((string) data ["tcp"]);
                                Close ();
@@ -977,9 +1008,11 @@ namespace System.Data.SqlClient {
                }
 
 #if NET_2_0
-               struct ColumnInfo {
+               struct ColumnInfo
+               {
                        public string name;
                        public Type type;
+
                        public ColumnInfo (string name, Type type)
                        {
                                this.name = name; this.type = type;
@@ -1103,6 +1136,7 @@ namespace System.Data.SqlClient {
                                new object [] {"Databases", 1, 1},
                                new object [] {"Tables", 4, 3},
                                new object [] {"Columns", 4, 4},
+                               new object [] {"StructuredTypeMembers", 4, 4},
                                new object [] {"Views", 3, 3},
                                new object [] {"ViewColumns", 4, 4},
                                new object [] {"ProcedureParameters", 4, 1},
@@ -1117,7 +1151,7 @@ namespace System.Data.SqlClient {
                        static public DataTable Instance {
                                get {
                                        if (instance == null) {
-                                               instance = new DataTable ("GetSchema");
+                                               instance = new DataTable ("MetaDataCollections");
                                                foreach (ColumnInfo c in columns)
                                                        instance.Columns.Add (c.name, c.type);
                                                foreach (object [] row in rows)
@@ -1128,6 +1162,57 @@ namespace System.Data.SqlClient {
                        }
                }
 
+               static class DataSourceInformation
+               {
+                       static readonly ColumnInfo [] columns = {
+                               new ColumnInfo ("CompositeIdentifierSeparatorPattern", typeof (string)),
+                               new ColumnInfo ("DataSourceProductName", typeof(string)),
+                               new ColumnInfo ("DataSourceProductVersion", typeof(string)),
+                               new ColumnInfo ("DataSourceProductVersionNormalized", typeof(string)),
+                               new ColumnInfo ("GroupByBehavior", typeof(GroupByBehavior)),
+                               new ColumnInfo ("IdentifierPattern", typeof(string)),
+                               new ColumnInfo ("IdentifierCase", typeof(IdentifierCase)),
+                               new ColumnInfo ("OrderByColumnsInSelect", typeof(bool)),
+                               new ColumnInfo ("ParameterMarkerFormat", typeof(string)),
+                               new ColumnInfo ("ParameterMarkerPattern", typeof(string)),
+                               new ColumnInfo ("ParameterNameMaxLength", typeof(int)),
+                               new ColumnInfo ("ParameterNamePattern", typeof(string)),
+                               new ColumnInfo ("QuotedIdentifierPattern", typeof(string)),
+                               new ColumnInfo ("QuotedIdentifierCase", typeof(IdentifierCase)),
+                               new ColumnInfo ("StatementSeparatorPattern", typeof(string)),
+                               new ColumnInfo ("StringLiteralPattern", typeof(string)),
+                               new ColumnInfo ("SupportedJoinOperators", typeof(SupportedJoinOperators))
+                       };
+
+                       static public DataTable GetInstance (SqlConnection conn)
+                       {
+                               DataTable table = new DataTable ("DataSourceInformation");
+                               foreach (ColumnInfo c in columns)
+                                       table.Columns.Add (c.name, c.type);
+                               DataRow row = table.NewRow ();
+                               row [0] = "\\.";
+                               row [1] = "Microsoft SQL Server";
+                               row [2] = conn.ServerVersion;;
+                               row [3] = conn.ServerVersion;;
+                               row [4] = GroupByBehavior.Unrelated;
+                               row [5] = @"(^\[\p{Lo}\p{Lu}\p{Ll}_@#][\p{Lo}\p{Lu}\p{Ll}\p{Nd}@$#_]*$)|(^\[[^\]\0]|\]\]+\]$)|(^\""[^\""\0]|\""\""+\""$)";
+                               row [6] = IdentifierCase.Insensitive; // FIXME: obtain this from SQL Server
+                               row [7] = false;
+                               row [8] = "{0}";
+                               row [9] = @"@[\p{Lo}\p{Lu}\p{Ll}\p{Lm}_@#][\p{Lo}\p{Lu}\p{Ll}\p{Lm}\p{Nd}\uff3f_@#\$]*(?=\s+|$)";
+                               row [10] = 128;
+                               row [11] = @"^[\p{Lo}\p{Lu}\p{Ll}\p{Lm}_@#][\p{Lo}\p{Lu}\p{Ll}\p{Lm}\p{Nd}\uff3f_@#\$]*(?=\s+|$)";
+                               row [12] = @"(([^\[]|\]\])*)";
+                               row [13] = IdentifierCase.Insensitive;  // FIXME: obtain this from SQL Server
+                               row [14] =";";
+                               row [15] = "'(([^']|'')*)'";
+                               row [16] = (SupportedJoinOperators.FullOuter | SupportedJoinOperators.Inner |
+                                       SupportedJoinOperators.LeftOuter | SupportedJoinOperators.RightOuter);
+                               table.Rows.Add (row);
+                               return table;
+                       }
+               }
+
                static class DataTypes
                {
                        static readonly ColumnInfo [] columns = {
@@ -1240,7 +1325,23 @@ namespace System.Data.SqlClient {
                                               null, "0x", null},
                                new object [] {"uniqueidentifier", 14, 16, "uniqueidentifier", null,
                                               "System.Guid", false, true, false, true, false, false, true,
-                                              true, false, null, null, null, false, null, "'", "'"}
+                                              true, false, null, null, null, false, null, "'", "'"},
+                               new object [] {"date", 31, 3L, "date", DBNull.Value,
+                                              "System.DateTime", false, false, false, true, true, false,
+                                              true, true, true, DBNull.Value, DBNull.Value, DBNull.Value,
+                                              false, DBNull.Value, "{ts '", "'}"},
+                               new object [] {"time", 32, 5L, "time({0})", "scale",
+                                              "System.TimeSpan", false, false, false, false, false, false,
+                                              true, true, true, DBNull.Value, (short) 7, (short) 0,
+                                              false, DBNull.Value, "{ts '", "'}"},
+                               new object [] {"datetime2", 33, 8L, "datetime2({0})", "scale",
+                                              "System.DateTime", false, true, false, false, false, false,
+                                              true, true, true, DBNull.Value, (short) 7, (short) 0,
+                                              false, DBNull.Value, "{ts '", "'}"},
+                               new object [] {"datetimeoffset", 34, 10L, "datetimeoffset({0})",
+                                              "scale", "System.DateTimeOffset", false, true, false, false,
+                                              false, false, true, true, true, DBNull.Value, (short) 7, (short) 0,
+                                              false, DBNull.Value, "{ts '", "'}"}
                        };
 
                        static DataTable instance;
@@ -1282,6 +1383,11 @@ namespace System.Data.SqlClient {
                                new object [] {"Columns", "Table", "@Table", "TABLE_NAME", 3},
                                new object [] {"Columns", "Column", "@Column", "COLUMN_NAME", 4},
 
+                               new object [] {"StructuredTypeMembers", "Catalog", "@Catalog", "TYPE_CATALOG", 1},
+                               new object [] {"StructuredTypeMembers", "Owner", "@Owner", "TYPE_SCHEMA", 2},
+                               new object [] {"StructuredTypeMembers", "Type", "@Type", "TYPE_NAME", 3},
+                               new object [] {"StructuredTypeMembers", "Member", "@Member", "MEMBER_NAME", 4},
+
                                new object [] {"Views", "Catalog", "@Catalog", "TABLE_CATALOG", 1},
                                new object [] {"Views", "Owner", "@Owner", "TABLE_SCHEMA", 2},
                                new object [] {"Views", "Table", "@Table", "TABLE_NAME", 3},
@@ -1301,14 +1407,14 @@ namespace System.Data.SqlClient {
                                new object [] {"Procedures", "Name", "@Name", "SPECIFIC_NAME", 3},
                                new object [] {"Procedures", "Type", "@Type", "ROUTINE_TYPE", 4},
 
-                               new object [] {"IndexColumns", "Catalog", "@Catalog", "db_name(}", 1},
-                               new object [] {"IndexColumns", "Owner", "@Owner", "user_name(}", 2},
+                               new object [] {"IndexColumns", "Catalog", "@Catalog", "db_name()", 1},
+                               new object [] {"IndexColumns", "Owner", "@Owner", "user_name()", 2},
                                new object [] {"IndexColumns", "Table", "@Table", "o.name", 3},
                                new object [] {"IndexColumns", "ConstraintName", "@ConstraintName", "x.name", 4},
                                new object [] {"IndexColumns", "Column", "@Column", "c.name", 5},
 
-                               new object [] {"Indexes", "Catalog", "@Catalog", "db_name(}", 1},
-                               new object [] {"Indexes", "Owner", "@Owner", "user_name(}", 2},
+                               new object [] {"Indexes", "Catalog", "@Catalog", "db_name()", 1},
+                               new object [] {"Indexes", "Owner", "@Owner", "user_name()", 2},
                                new object [] {"Indexes", "Table", "@Table", "o.name", 3},
                                new object [] {"Indexes", "Name", "@Name", "x.name", 4},
 
@@ -1338,6 +1444,9 @@ namespace System.Data.SqlClient {
 
                public override DataTable GetSchema ()
                {
+                       if (state == ConnectionState.Closed)
+                               throw ExceptionHelper.ConnectionClosed ();
+
                        return MetaDataCollections.Instance;
                }
 
@@ -1348,11 +1457,12 @@ namespace System.Data.SqlClient {
 
                public override DataTable GetSchema (String collectionName, string [] restrictionValues)
                {
-                       if (collectionName == null)
-                               //LAMESPEC: In MS.NET, if collectionName is null, it throws ArgumentException.
-                               throw new ArgumentException ();
+                       // LAMESPEC: In MS.NET, if collectionName is null, it throws ArgumentException.
 
-                       String cName          = null;
+                       if (state == ConnectionState.Closed)
+                               throw ExceptionHelper.ConnectionClosed ();
+
+                       String cName = null;
                        DataTable schemaTable = MetaDataCollections.Instance;
                        int length = restrictionValues == null ? 0 : restrictionValues.Length;
 
@@ -1366,8 +1476,11 @@ namespace System.Data.SqlClient {
                                        cName = row["CollectionName"].ToString();
                                }
                        }
+
                        if (cName == null)
-                               throw new ArgumentException ("The requested collection ('" + collectionName + "') is not defined.");
+                               throw new ArgumentException (string.Format (CultureInfo.InvariantCulture,
+                                       "The requested collection ({0}) is not defined.",
+                                       collectionName));
 
                        SqlCommand command     = null;
                        DataTable dataTable    = new DataTable ();
@@ -1499,6 +1612,17 @@ namespace System.Data.SqlClient {
                                                          " where (name = @Name or (@Name is null))", this);
                                command.Parameters.Add ("@Name", SqlDbType.NVarChar, 4000);
                                break;
+                       case "StructuredTypeMembers":
+                               // Only available on SQL Server 2008
+                               // Running it again SQL 2005 results in the following exception:
+                               // Unable to build the 'StructuredTypeMembers' collection because
+                               // execution of the SQL query failed. See the inner exception for details.
+                               // ---> System.Data.SqlClient.SqlException: Invalid object name 'sys.table_types'.
+                               // 
+                               // I don't have access to SQL Server 2008 right now,
+                               // and can't find any online documentation on the 'sys.table_types'
+                               // view
+                               throw new NotImplementedException ();
                        case "Views":
                                command = new SqlCommand ("select TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, CHECK_OPTION, " +
                                                          "IS_UPDATABLE from INFORMATION_SCHEMA.VIEWS where (TABLE_CATALOG" +
@@ -1542,7 +1666,7 @@ namespace System.Data.SqlClient {
                        case "MetaDataCollections":
                                return MetaDataCollections.Instance;
                        case "DataSourceInformation":
-                               throw new NotImplementedException ();
+                               return DataSourceInformation.GetInstance (this);
                        case "DataTypes":
                                return DataTypes.Instance;
                        case "ReservedWords":
@@ -1560,16 +1684,43 @@ namespace System.Data.SqlClient {
                
                public static void ChangePassword (string connectionString, string newPassword)
                {
-                       if (connectionString == null || newPassword == null || newPassword == String.Empty)
-                               throw new ArgumentNullException ();
+                       if (String.IsNullOrEmpty (connectionString))
+                               throw new ArgumentNullException ("The 'connectionString' cannot be null or empty.");
+                       if (String.IsNullOrEmpty (newPassword))
+                               throw new ArgumentNullException ("The 'newPassword' cannot be null or empty.");
                        if (newPassword.Length > 128)
-                               throw new ArgumentException ("The value of newPassword exceeds its permittable length which is 128");
+                               throw new ArgumentException ("The length of 'newPassword' cannot exceed 128 characters.");
                        using (SqlConnection conn = new SqlConnection (connectionString)) {
                                conn.Open ();
                                conn.tds.Execute (String.Format ("sp_password '{0}', '{1}', '{2}'",
                                                                 conn.parms.Password, newPassword, conn.parms.User));
                        }
                }
+
+               public static void ClearAllPools ()
+               {
+                       // FIXME: locking
+                       IDictionary pools = SqlConnection.sqlConnectionPools.GetConnectionPool ();
+                       foreach (TdsConnectionPool pool in pools.Values) {
+                               if (pool != null)
+                                       pool.ResetConnectionPool ();
+                       }
+                       pools.Clear ();
+               }
+
+               public static void ClearPool (SqlConnection connection)
+               {
+                       if (connection == null)
+                               throw new ArgumentNullException ("connection");
+
+                       // FIXME: locking
+                       if (connection.pooling) {
+                               TdsConnectionPool pool = sqlConnectionPools.GetConnectionPool (connection.ConnectionString);
+                               if (pool != null)
+                                       pool.ResetConnectionPool ();
+                       }
+               }
+
 #endif // NET_2_0
 
                #endregion // Methods
@@ -1577,13 +1728,14 @@ namespace System.Data.SqlClient {
 #if NET_2_0
                #region Fields Net 2
 
-               bool async = false;
+               bool async;
+               bool userInstance;
 
                #endregion // Fields  Net 2
 
                #region Properties Net 2
 
-#if !NET_2_0
+#if NET_1_0
                [DataSysDescription ("Enable Asynchronous processing, 'Asynchrouse Processing=true/false' in the ConnectionString.")]   
 #endif
                [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]