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
#endif // NET_2_0
{
#region Fields
+
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;
// Connection parameters
- TdsConnectionParameters parms = new TdsConnectionParameters ();
- NameValueCollection connStringParameters;
+ TdsConnectionParameters parms;
bool connectionReset;
bool pooling;
string dataSource;
int port;
bool fireInfoMessageEventOnUserErrors;
bool statisticsEnabled;
-
+
// The current state
ConnectionState state = ConnectionState.Closed;
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 = DEFAULT_CONNECTIONTIMEOUT;
- dataSource = string.Empty;
- packetSize = DEFAULT_PACKETSIZE;
- port = DEFAULT_PORT;
ConnectionString = connectionString;
}
[EditorAttribute ("Microsoft.VSDesigner.Data.SQL.Design.SqlConnectionStringEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]
[RecommendedAsConfigurable (true)]
[RefreshProperties (RefreshProperties.All)]
- public
+ public
#if NET_2_0
override
#endif // NET_2_0
set {
if (state == ConnectionState.Open)
throw new InvalidOperationException ("Not Allowed to change ConnectionString property while Connection state is OPEN");
- SetConnectionString (value);
+ SetConnectionString (value);
}
}
[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
[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
[Browsable(true)]
#endif
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
- public
+ public
#if NET_2_0
override
#endif // NET_2_0
[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
[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
get { return state; }
}
- internal ITds Tds {
+ internal Tds Tds {
get { return tds; }
}
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);
}
#region Methods
- internal string GetConnStringKeyValue (params string [] keys)
- {
- if (connStringParameters == null || connStringParameters.Count == 0)
- return string.Empty;
-
- foreach (string key in keys) {
- string value = connStringParameters [key];
- if (value != null)
- return value;
- }
-
- return string.Empty;
- }
-
public new SqlTransaction BeginTransaction ()
{
return BeginTransaction (IsolationLevel.ReadCommitted, String.Empty);
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));
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 ();
if (tds != null && tds.IsConnected) {
if (pooling && tds.Pooling) {
-#if NET_2_0
- if(pool != null) pool.ReleaseConnection (ref tds);
-#else
- if(pool != null) pool.ReleaseConnection (tds);
-#endif
- }else
- if(tds != null) tds.Disconnect ();
+ if (pool != null) {
+ pool.ReleaseConnection (tds);
+ pool = null;
+ }
+ } else {
+ tds.Disconnect ();
+ }
}
if (tds != null) {
ChangeState (ConnectionState.Closed);
}
- public new SqlCommand CreateCommand ()
+ public new SqlCommand CreateCommand ()
{
SqlCommand command = new SqlCommand ();
command.Connection = this;
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 = string.Empty;
- SetDefaultConnectionParameters (this.connStringParameters);
+ ConnectionString = null;
}
} finally {
disposed = true;
}
}
+#if !MOBILE
[MonoTODO ("Not sure what this means at present.")]
public void EnlistDistributedTransaction (ITransaction transaction)
{
throw new NotImplementedException ();
}
+#endif
object ICloneable.Clone ()
{
}
#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 ()
}
#endif
- public
+ public
#if NET_2_0
override
#endif // NET_2_0
- void Open ()
+ void Open ()
{
string serverName = string.Empty;
if (state == ConnectionState.Open)
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))
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.Empty;
string theInstanceName = string.Empty;
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.Length == 0 || theDataSource == "(local)")
- theServerName = "localhost";
- else
+ } else
theServerName = theDataSource;
+ if (theServerName.Length == 0 || theServerName == "(local)" || theServerName == ".")
+ theServerName = "localhost";
+
if ((idx = theServerName.IndexOf ("tcp:")) > -1)
theServerName = theServerName.Substring (idx + 4);
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)
{
+ if (value.Length == 0)
+ return defaultValue;
+
string upperValue = value.ToUpper ();
if (upperValue == "TRUE" || upperValue == "YES")
"Invalid value \"{0}\" for key '{1}'.", value, key));
}
- private int ConvertToInt32 (string key, string value)
+ private int ConvertToInt32 (string key, string value, int defaultValue)
{
+ 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));
+ "Invalid value \"{0}\" for key '{1}'.", value, key), ex);
}
}
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;
}
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");
}
}
+ 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 ();
+ if (parms == null)
+ parms = new TdsConnectionParameters ();
+ else
+ parms.Reset ();
dataSource = string.Empty;
connectionTimeout = DEFAULT_CONNECTIONTIMEOUT;
connectionReset = true;
maxPoolSize = DEFAULT_MAXPOOLSIZE;
minPoolSize = DEFAULT_MINPOOLSIZE;
packetSize = DEFAULT_PACKETSIZE;
-
- parameters["APPLICATION NAME"] = "Mono SqlClient Data Provider";
- parameters["CONNECT TIMEOUT"] = connectionTimeout.ToString (CultureInfo.InvariantCulture);
- parameters["CONNECTION LIFETIME"] = "0";
- parameters["CONNECTION RESET"] = "true";
- parameters["ENLIST"] = "true";
- parameters["INTEGRATED SECURITY"] = "false";
- parameters["INITIAL CATALOG"] = string.Empty;
- parameters["MAX POOL SIZE"] = maxPoolSize.ToString (CultureInfo.InvariantCulture);
- parameters["MIN POOL SIZE"] = minPoolSize.ToString (CultureInfo.InvariantCulture);
- parameters["NETWORK LIBRARY"] = "dbmssocn";
- parameters["PACKET SIZE"] = packetSize.ToString (CultureInfo.InvariantCulture);
- parameters["PERSIST SECURITY INFO"] = "false";
- parameters["POOLING"] = "true";
- parameters["WORKSTATION ID"] = Environment.MachineName;
+ 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" :
+ 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;
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 + "'.");
}
}
}
#endif
- private sealed class SqlMonitorSocket : UdpClient
+ private sealed class SqlMonitorSocket : UdpClient
{
// UDP port that the SQL Monitor listens
private static readonly int SqlMonitorUdpPort = 1434;
instance = InstanceName;
}
- internal int DiscoverTcpPort (int timeoutSeconds)
+ internal int DiscoverTcpPort (int timeoutSeconds)
{
int SqlServerTcpPort;
Client.Blocking = false;
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
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 ();
}
#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;
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}'",
public static void ClearAllPools ()
{
- Hashtable pools = SqlConnection.sqlConnectionPools.GetConnectionPool ();
+ // FIXME: locking
+ IDictionary pools = SqlConnection.sqlConnectionPools.GetConnectionPool ();
foreach (TdsConnectionPool pool in pools.Values) {
- if (pool != null) {
+ if (pool != null)
pool.ResetConnectionPool ();
- ITds tds = pool.GetConnection ();
- tds.Pooling = false;
- }
}
+ pools.Clear ();
}
public static void ClearPool (SqlConnection connection)
{
+ if (connection == null)
+ throw new ArgumentNullException ("connection");
+
+ // FIXME: locking
if (connection.pooling) {
- connection.pooling = false;
- if (connection.pool != null)
- connection.pool.ResetConnectionPool (connection.Tds);
+ TdsConnectionPool pool = sqlConnectionPools.GetConnectionPool (connection.ConnectionString);
+ if (pool != null)
+ pool.ResetConnectionPool ();
}
}
#if NET_2_0
#region Fields Net 2
- bool async = false;
+ bool async;
+ bool userInstance;
#endregion // Fields Net 2