//
// Author:
// Tim Coleman (tim@timcoleman.com)
+// Daniel Morgan (danmorg@sc.rr.com)
//
-// Copyright (C) Tim Coleman, 2002
+// Copyright (C) Tim Coleman, 2002-2003
+// Copyright (C) Daniel Morgan, 2003
//
-using Mono.Data.TdsClient.Internal;
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using Mono.Data.Tds.Protocol;
using System;
using System.Collections;
using System.Collections.Specialized;
using System.Data.Common;
using System.EnterpriseServices;
using System.Net;
+using System.Net.Sockets;
using System.Text;
namespace Mono.Data.SybaseClient {
public sealed class SybaseConnection : Component, IDbConnection, ICloneable
{
#region Fields
+ bool disposed = false;
// The set of SQL connection pools
- static Hashtable SybaseConnectionPools = new Hashtable ();
+ static TdsConnectionPoolManager sybaseConnectionPools = new TdsConnectionPoolManager (TdsVersion.tds50);
// The current connection pool
- SybaseConnectionPool pool;
+ TdsConnectionPool pool;
// The connection string that identifies this connection
string connectionString = null;
int minPoolSize;
int maxPoolSize;
int packetSize;
- int port = 1533;
+ int port = 2048;
// The current state
ConnectionState state = ConnectionState.Closed;
- bool dataReaderOpen = false;
+ SybaseDataReader dataReader = null;
// The TDS object
ITds tds;
public SybaseConnection (string connectionString)
{
- SetConnectionString (connectionString);
- this.connectionString = connectionString;
+ ConnectionString = connectionString;
}
#endregion // Constructors
get { return tds.Database; }
}
- internal bool DataReaderOpen {
- get { return dataReaderOpen; }
- set { dataReaderOpen = value; }
+ internal SybaseDataReader DataReader {
+ get { return dataReader; }
+ set { dataReader = value; }
}
public string DataSource {
internal SybaseTransaction Transaction {
get { return transaction; }
+ set { transaction = value; }
}
public string WorkstationId {
#endregion // Properties
+ #region Events and Delegates
+
+ public event SybaseInfoMessageEventHandler InfoMessage;
+ public event StateChangeEventHandler StateChange;
+
+ private void ErrorHandler (object sender, TdsInternalErrorMessageEventArgs e)
+ {
+ throw new SybaseException (e.Class, e.LineNumber, e.Message, e.Number, e.Procedure, e.Server, "Mono SybaseClient Data Provider", e.State);
+ }
+
+ private void MessageHandler (object sender, TdsInternalInfoMessageEventArgs e)
+ {
+ OnSybaseInfoMessage (CreateSybaseInfoMessageEvent (e.Errors));
+ }
+
+ #endregion // Events and Delegates
+
#region Methods
public SybaseTransaction BeginTransaction ()
public SybaseTransaction BeginTransaction (string transactionName)
{
- return BeginTransaction (IsolationLevel.ReadCommitted, String.Empty);
+ return BeginTransaction (IsolationLevel.ReadCommitted, transactionName);
}
public SybaseTransaction BeginTransaction (IsolationLevel iso, string transactionName)
{
- if (transaction != null)
+ if (State == ConnectionState.Closed)
+ throw new InvalidOperationException ("The connection is not open.");
+ if (Transaction != null)
throw new InvalidOperationException ("SybaseConnection does not support parallel transactions.");
- tds.ExecuteNonQuery (String.Format ("BEGIN TRANSACTION {0}", transactionName));
- CheckForErrors ();
+ string isolevel = String.Empty;
+ switch (iso) {
+ case IsolationLevel.Chaos:
+ isolevel = "CHAOS";
+ break;
+ case IsolationLevel.ReadCommitted:
+ isolevel = "READ COMMITTED";
+ break;
+ case IsolationLevel.ReadUncommitted:
+ isolevel = "READ UNCOMMITTED";
+ break;
+ case IsolationLevel.RepeatableRead:
+ isolevel = "REPEATABLE READ";
+ break;
+ case IsolationLevel.Serializable:
+ isolevel = "SERIALIZABLE";
+ break;
+ }
+ tds.Execute (String.Format ("SET TRANSACTION ISOLATION LEVEL {0}\nBEGIN TRANSACTION {1}", isolevel, transactionName));
transaction = new SybaseTransaction (this, iso);
return transaction;
}
{
if (!IsValidDatabaseName (database))
throw new ArgumentException (String.Format ("The database name {0} is not valid."));
-
- if (state != ConnectionState.Open)
+ if (State != ConnectionState.Open)
throw new InvalidOperationException ("The connection is not open");
-
- tds.ExecuteNonQuery (String.Format ("use {0}", database));
- CheckForErrors ();
+ tds.Execute (String.Format ("use {0}", database));
}
- internal void CheckForErrors ()
+ private void ChangeState (ConnectionState currentState)
{
- if (tds.Errors.Count > 0)
- throw SybaseException.FromTdsError (tds.Errors);
- }
+ ConnectionState originalState = state;
+ state = currentState;
+ OnStateChange (CreateStateChangeEvent (originalState, currentState));
+ }
public void Close ()
{
- if (transaction != null && transaction.IsOpen)
- transaction.Rollback ();
+ if (Transaction != null && Transaction.IsOpen)
+ Transaction.Rollback ();
if (pooling)
pool.ReleaseConnection (tds);
else
tds.Disconnect ();
- this.state = ConnectionState.Closed;
+ tds.TdsErrorMessage -= new TdsInternalErrorMessageEventHandler (ErrorHandler);
+ tds.TdsInfoMessage -= new TdsInternalInfoMessageEventHandler (MessageHandler);
+ ChangeState (ConnectionState.Closed);
}
public SybaseCommand CreateCommand ()
return command;
}
- protected override void Dispose (bool disposing)
+ private StateChangeEventArgs CreateStateChangeEvent (ConnectionState originalState, ConnectionState currentState)
{
- Close ();
+ return new StateChangeEventArgs (originalState, currentState);
+ }
+
+ private SybaseInfoMessageEventArgs CreateSybaseInfoMessageEvent (TdsInternalErrorCollection errors)
+ {
+ return new SybaseInfoMessageEventArgs (errors);
+ }
+
+ protected override void Dispose (bool disposing)
+ {
+ if (!disposed) {
+ if (disposing) {
+ if (State == ConnectionState.Open)
+ Close ();
+ parms = null;
+ dataSource = null;
+ }
+ base.Dispose (disposing);
+ disposed = true;
+ }
}
[MonoTODO]
throw new NotImplementedException ();
}
- [MonoTODO]
object ICloneable.Clone ()
{
- throw new NotImplementedException ();
+ return new SybaseConnection (ConnectionString);
}
IDbTransaction IDbConnection.BeginTransaction ()
void IDisposable.Dispose ()
{
- Dispose ();
+ Dispose (true);
+ GC.SuppressFinalize (this);
}
+ [MonoTODO ("Figure out the Sybase way to reset the connection.")]
public void Open ()
{
- if (connectionString == null)
+ string serverName = "";
+ if (connectionString == null || connectionString.Equals (""))
throw new InvalidOperationException ("Connection string has not been initialized.");
- if (!pooling)
- tds = new Tds50 (dataSource, port, packetSize);
- else {
- pool = (SybaseConnectionPool) SybaseConnectionPools [connectionString];
- if (pool == null) {
- pool = new SybaseConnectionPool (dataSource, port, packetSize, minPoolSize, maxPoolSize);
- SybaseConnectionPools [connectionString] = pool;
+
+ try {
+ if (!pooling) {
+ ParseDataSource (dataSource, out port, out serverName);
+ tds = new Tds50 (serverName, port, PacketSize, ConnectionTimeout);
+ }
+ else {
+ ParseDataSource (dataSource, out port, out serverName);
+ TdsConnectionInfo info = new TdsConnectionInfo (serverName, port, packetSize, ConnectionTimeout, minPoolSize, maxPoolSize);
+ pool = sybaseConnectionPools.GetConnectionPool (connectionString, info);
+ tds = pool.GetConnection ();
}
- tds = pool.AllocateConnection ();
+ }
+ catch (TdsTimeoutException e) {
+ throw SybaseException.FromTdsInternalException ((TdsInternalException) e);
}
- state = ConnectionState.Open;
+ tds.TdsErrorMessage += new TdsInternalErrorMessageEventHandler (ErrorHandler);
+ tds.TdsInfoMessage += new TdsInternalInfoMessageEventHandler (MessageHandler);
if (!tds.IsConnected) {
- tds.Connect (parms);
- CheckForErrors ();
- ChangeDatabase (parms.Database);
- }
+ try {
+ tds.Connect (parms);
+ ChangeState (ConnectionState.Open);
+ ChangeDatabase (parms.Database);
+ }
+ catch {
+ if (pooling)
+ pool.ReleaseConnection (tds);
+ throw;
+ }
+ }
else if (connectionReset) {
- tds.ExecuteNonQuery ("EXEC sp_connection_reset");
- CheckForErrors ();
+ // tds.ExecuteNonQuery ("EXEC sp_reset_connection"); FIXME
+ ChangeState (ConnectionState.Open);
}
}
- void SetConnectionString (string connectionString)
- {
- connectionString += ";";
- NameValueCollection parameters = new NameValueCollection ();
-
- if (connectionString == String.Empty)
- return;
-
- bool inQuote = false;
- bool inDQuote = false;
-
- string name = String.Empty;
- string value = String.Empty;
- StringBuilder sb = new StringBuilder ();
-
- foreach (char c in connectionString)
- {
- switch (c) {
- case '\'':
- inQuote = !inQuote;
- break;
- case '"' :
- inDQuote = !inDQuote;
- break;
- case ';' :
- if (!inDQuote && !inQuote) {
- if (name != String.Empty && name != null) {
- value = sb.ToString ();
- parameters [name.ToUpper ().Trim ()] = value.Trim ();
- }
- name = String.Empty;
- value = String.Empty;
- sb = new StringBuilder ();
- }
- else
- sb.Append (c);
- break;
- case '=' :
- if (!inDQuote && !inQuote) {
- name = sb.ToString ();
- sb = new StringBuilder ();
- }
- else
- sb.Append (c);
- break;
- default:
- sb.Append (c);
- break;
- }
- }
-
- if (this.ConnectionString == null)
- {
- SetDefaultConnectionParameters (parameters);
- }
-
- SetProperties (parameters);
-
- this.connectionString = connectionString;
- }
-
- void SetDefaultConnectionParameters (NameValueCollection parameters)
- {
- if (null == parameters.Get ("APPLICATION NAME"))
- parameters["APPLICATION NAME"] = ".Net SybaseClient Data Provider";
- if (null == parameters.Get ("CONNECT TIMEOUT") && null == parameters.Get ("CONNECTION TIMEOUT"))
- parameters["CONNECT TIMEOUT"] = "15";
- if (null == parameters.Get ("CONNECTION LIFETIME"))
- parameters["CONNECTION LIFETIME"] = "0";
- if (null == parameters.Get ("CONNECTION RESET"))
- parameters["CONNECTION RESET"] = "true";
- if (null == parameters.Get ("ENLIST"))
- parameters["ENLIST"] = "true";
- if (null == parameters.Get ("INTEGRATED SECURITY") && null == parameters.Get ("TRUSTED_CONNECTION"))
- parameters["INTEGRATED SECURITY"] = "false";
- if (null == parameters.Get ("MAX POOL SIZE"))
- parameters["MAX POOL SIZE"] = "100";
- if (null == parameters.Get ("MIN POOL SIZE"))
- parameters["MIN POOL SIZE"] = "0";
- if (null == parameters.Get ("NETWORK LIBRARY") && null == parameters.Get ("NET"))
- parameters["NETWORK LIBRARY"] = "dbmssocn";
- if (null == parameters.Get ("PACKET SIZE"))
- parameters["PACKET SIZE"] = "512";
- if (null == parameters.Get ("PERSIST SECURITY INFO"))
- parameters["PERSIST SECURITY INFO"] = "false";
- if (null == parameters.Get ("POOLING"))
- parameters["POOLING"] = "true";
- if (null == parameters.Get ("WORKSTATION ID"))
- parameters["WORKSTATION ID"] = Dns.GetHostByName ("localhost").HostName;
- }
-
- private void SetProperties (NameValueCollection parameters)
- {
- string value;
- foreach (string name in parameters) {
- value = parameters[name];
-
- switch (name) {
- case "APPLICATION NAME" :
- parms.ApplicationName = value;
- break;
- case "ATTACHDBFILENAME" :
- case "EXTENDED PROPERTIES" :
- case "INITIAL FILE NAME" :
- break;
- case "CONNECT TIMEOUT" :
- case "CONNECTION TIMEOUT" :
- connectionTimeout = Int32.Parse (value);
- break;
- case "CONNECTION LIFETIME" :
- break;
- case "CONNECTION RESET" :
- connectionReset = !(value.ToUpper ().Equals ("FALSE") || value.ToUpper ().Equals ("NO"));
- break;
- case "CURRENT LANGUAGE" :
- parms.Language = value;
- break;
- case "DATA SOURCE" :
- case "SERVER" :
- case "ADDRESS" :
- case "ADDR" :
- case "NETWORK ADDRESS" :
- dataSource = value;
- break;
- case "ENLIST" :
- break;
- case "INITIAL CATALOG" :
- case "DATABASE" :
- parms.Database = value;
- break;
- case "INTEGRATED SECURITY" :
- case "TRUSTED_CONNECTION" :
- break;
- case "MAX POOL SIZE" :
- maxPoolSize = Int32.Parse (value);
- break;
- case "MIN POOL SIZE" :
- minPoolSize = Int32.Parse (value);
- break;
- case "NET" :
- case "NETWORK LIBRARY" :
- if (!value.ToUpper ().Equals ("DBMSSOCN"))
- throw new ArgumentException ("Unsupported network library.");
- break;
- case "PACKET SIZE" :
- packetSize = Int32.Parse (value);
- break;
- case "PASSWORD" :
- case "PWD" :
- parms.Password = value;
- break;
- case "PERSIST SECURITY INFO" :
- break;
- case "POOLING" :
- pooling = !(value.ToUpper ().Equals ("FALSE") || value.ToUpper ().Equals ("NO"));
- break;
- case "USER ID" :
- parms.User = value;
- break;
- case "WORKSTATION ID" :
- parms.Hostname = value;
- break;
- }
+ private void ParseDataSource (string theDataSource, out int thePort, out string theServerName)
+ {
+ theServerName = "";
+ thePort = 2048;
+
+ int idx = 0;
+ if ((idx = theDataSource.IndexOf (",")) > -1) {
+ theServerName = theDataSource.Substring (0, idx);
+ string p = theDataSource.Substring (idx + 1);
+ thePort = Int32.Parse (p);
}
+ else {
+ theServerName = theDataSource;
+ }
+ }
+
+ private string ParseValue (string name, string value)
+ {
+ if (name.Length == 0 && value.Length > 0)
+ throw new ArgumentException ("Expected '=' delimiter while parsing connection value pair.");
+ if (name.Length > 0)
+ return value.Trim ();
+ return String.Empty;
+ }
+
+ private void SetConnectionString (string connectionString)
+ {
+ if (connectionString == String.Empty) {
+ this.connectionString = connectionString;
+ return;
+ }
+
+ NameValueCollection parameters = new NameValueCollection ();
+
+ string name = String.Empty;
+ string value = String.Empty;
+ StringBuilder sb = new StringBuilder ();
+
+ char delimiter = '\0';
+
+ foreach (char c in connectionString) {
+ switch (c) {
+ case '\'' :
+ case '"' :
+ if (delimiter.Equals (c))
+ delimiter = '\0';
+ else if (delimiter.Equals ('\0'))
+ delimiter = c;
+ else
+ sb.Append (c);
+ break;
+ case ';' :
+ if (delimiter.Equals ('\0')) {
+ value = ParseValue (name, sb.ToString ());
+ if (!value.Equals (""))
+ parameters [name.ToUpper ().Trim ()] = value;
+ name = String.Empty;
+ sb = new StringBuilder ();
+ }
+ else
+ sb.Append (c);
+ break;
+ case '=' :
+ if (delimiter.Equals ('\0')) {
+ name = sb.ToString ();
+ sb = new StringBuilder ();
+ }
+ else
+ sb.Append (c);
+ break;
+ default:
+ sb.Append (c);
+ break;
+ }
+ }
+
+ if (!delimiter.Equals ('\0'))
+ throw new ArgumentException (String.Format ("Matching end delimiter {0} not found in connection option value.", delimiter));
+
+ value = ParseValue (name, sb.ToString ());
+ if (!value.Equals (""))
+ parameters [name.ToUpper ().Trim ()] = value;
+
+ SetDefaultConnectionParameters (parameters);
+ SetProperties (parameters);
+
+ this.connectionString = connectionString;
+ }
+
+ private void SetDefaultConnectionParameters (NameValueCollection parameters)
+ {
+ if (null == parameters.Get ("APPLICATION NAME"))
+ parameters["APPLICATION NAME"] = "Mono SybaseClient Data Provider";
+ if (null == parameters.Get ("CONNECT TIMEOUT") && null == parameters.Get ("CONNECTION TIMEOUT")) {
+ parameters["CONNECT TIMEOUT"] = "15";
+ connectionTimeout = 15;
+ }
+ if (null == parameters.Get ("CONNECTION LIFETIME"))
+ parameters["CONNECTION LIFETIME"] = "0";
+ if (null == parameters.Get ("CONNECTION RESET"))
+ parameters["CONNECTION RESET"] = "true";
+ if (null == parameters.Get ("ENLIST"))
+ parameters["ENLIST"] = "true";
+ if (null == parameters.Get ("INTEGRATED SECURITY") && null == parameters.Get ("TRUSTED_CONNECTION"))
+ parameters["INTEGRATED SECURITY"] = "false";
+ if (null == parameters.Get ("MAX POOL SIZE")) {
+ parameters["MAX POOL SIZE"] = "100";
+ maxPoolSize = 100;
+ }
+ if (null == parameters.Get ("MIN POOL SIZE")) {
+ parameters["MIN POOL SIZE"] = "0";
+ maxPoolSize = 0;
+ }
+ if (null == parameters.Get ("NETWORK LIBRARY") && null == parameters.Get ("NET"))
+ parameters["NETWORK LIBRARY"] = "dbmssocn";
+ if (null == parameters.Get ("PACKET SIZE")) {
+ parameters["PACKET SIZE"] = "512";
+ packetSize = 512;
+ }
+ if (null == parameters.Get ("PERSIST SECURITY INFO"))
+ parameters["PERSIST SECURITY INFO"] = "false";
+ if (null == parameters.Get ("POOLING"))
+ parameters["POOLING"] = "true";
+ if (null == parameters.Get ("WORKSTATION ID"))
+ parameters["WORKSTATION ID"] = Dns.GetHostByName ("localhost").HostName;
}
+ private void SetProperties (NameValueCollection parameters)
+ {
+ string value;
+ foreach (string name in parameters) {
+ value = parameters [name];
+
+ switch (name) {
+ case "APPLICATION NAME" :
+ parms.ApplicationName = value;
+ break;
+ case "ATTACHDBFILENAME" :
+ case "EXTENDED PROPERTIES" :
+ case "INITIAL FILE NAME" :
+ break;
+ case "CONNECT TIMEOUT" :
+ case "CONNECTION TIMEOUT" :
+ connectionTimeout = Int32.Parse (value);
+ break;
+ case "CONNECTION LIFETIME" :
+ break;
+ case "CONNECTION RESET" :
+ connectionReset = !(value.ToUpper ().Equals ("FALSE") || value.ToUpper ().Equals ("NO"));
+ break;
+ case "CURRENT LANGUAGE" :
+ parms.Language = value;
+ break;
+ case "DATA SOURCE" :
+ case "SERVER" :
+ case "ADDRESS" :
+ case "ADDR" :
+ case "NETWORK ADDRESS" :
+ dataSource = value;
+ break;
+ case "ENLIST" :
+ break;
+ case "INITIAL CATALOG" :
+ case "DATABASE" :
+ parms.Database = value;
+ break;
+ case "INTEGRATED SECURITY" :
+ case "TRUSTED_CONNECTION" :
+ break;
+ case "MAX POOL SIZE" :
+ maxPoolSize = Int32.Parse (value);
+ break;
+ case "MIN POOL SIZE" :
+ minPoolSize = Int32.Parse (value);
+ break;
+ case "NET" :
+ case "NETWORK LIBRARY" :
+ if (!value.ToUpper ().Equals ("DBMSSOCN"))
+ throw new ArgumentException ("Unsupported network library.");
+ break;
+ case "PACKET SIZE" :
+ packetSize = Int32.Parse (value);
+ break;
+ case "PASSWORD" :
+ case "PWD" :
+ parms.Password = value;
+ break;
+ case "PERSIST SECURITY INFO" :
+ break;
+ case "POOLING" :
+ pooling = !(value.ToUpper ().Equals ("FALSE") || value.ToUpper ().Equals ("NO"));
+ break;
+ case "USER ID" :
+ parms.User = value;
+ break;
+ case "WORKSTATION ID" :
+ parms.Hostname = value;
+ break;
+ }
+ }
+ }
- static bool IsValidDatabaseName (string database)
+ private static bool IsValidDatabaseName (string database)
{
if (database.Length > 32 || database.Length < 1)
return false;
return true;
}
- #endregion // Methods
+ private void OnSybaseInfoMessage (SybaseInfoMessageEventArgs value)
+ {
+ if (InfoMessage != null)
+ InfoMessage (this, value);
+ }
- #region Events
-
- public event SybaseInfoMessageEventHandler InfoMessage;
- public event StateChangeEventHandler StateChange;
-
- #endregion // Events
+ private void OnStateChange (StateChangeEventArgs value)
+ {
+ if (StateChange != null)
+ StateChange (this, value);
+ }
+
+ #endregion // Methods
}
}