2 // Mono.Data.SybaseClient.SybaseConnection.cs
5 // Tim Coleman (tim@timcoleman.com)
6 // Daniel Morgan (danmorg@sc.rr.com)
8 // Copyright (C) Tim Coleman, 2002, 2003
9 // Copyright (C) Daniel Morgan, 2003
12 using Mono.Data.Tds.Protocol;
14 using System.Collections;
15 using System.Collections.Specialized;
16 using System.ComponentModel;
18 using System.Data.Common;
19 using System.EnterpriseServices;
21 using System.Net.Sockets;
24 namespace Mono.Data.SybaseClient {
25 public sealed class SybaseConnection : Component, IDbConnection, ICloneable
28 bool disposed = false;
30 // The set of SQL connection pools
31 static Hashtable SybaseConnectionPools = new Hashtable ();
33 // The current connection pool
34 SybaseConnectionPool pool;
36 // The connection string that identifies this connection
37 string connectionString = null;
39 // The transaction object for the current transaction
40 SybaseTransaction transaction = null;
42 // Connection parameters
43 TdsConnectionParameters parms = new TdsConnectionParameters ();
47 int connectionTimeout;
54 ConnectionState state = ConnectionState.Closed;
56 SybaseDataReader dataReader = null;
65 public SybaseConnection ()
70 public SybaseConnection (string connectionString)
72 ConnectionString = connectionString;
75 #endregion // Constructors
79 public string ConnectionString {
80 get { return connectionString; }
81 set { SetConnectionString (value); }
84 public int ConnectionTimeout {
85 get { return connectionTimeout; }
88 public string Database {
89 get { return tds.Database; }
92 internal SybaseDataReader DataReader {
93 get { return dataReader; }
94 set { dataReader = value; }
97 public string DataSource {
98 get { return dataSource; }
101 public int PacketSize {
102 get { return packetSize; }
105 public string ServerVersion {
106 get { return tds.ServerVersion; }
109 public ConnectionState State {
110 get { return state; }
117 internal SybaseTransaction Transaction {
118 get { return transaction; }
119 set { transaction = value; }
122 public string WorkstationId {
123 get { return parms.Hostname; }
126 #endregion // Properties
128 #region Events and Delegates
130 public event SybaseInfoMessageEventHandler InfoMessage;
131 public event StateChangeEventHandler StateChange;
133 private void ErrorHandler (object sender, TdsInternalErrorMessageEventArgs e)
135 throw new SybaseException (e.Class, e.LineNumber, e.Message, e.Number, e.Procedure, e.Server, "Mono SybaseClient Data Provider", e.State);
138 private void MessageHandler (object sender, TdsInternalInfoMessageEventArgs e)
140 OnSybaseInfoMessage (CreateSybaseInfoMessageEvent (e.Errors));
143 #endregion // Events and Delegates
147 public SybaseTransaction BeginTransaction ()
149 return BeginTransaction (IsolationLevel.ReadCommitted, String.Empty);
152 public SybaseTransaction BeginTransaction (IsolationLevel iso)
154 return BeginTransaction (iso, String.Empty);
157 public SybaseTransaction BeginTransaction (string transactionName)
159 return BeginTransaction (IsolationLevel.ReadCommitted, transactionName);
162 public SybaseTransaction BeginTransaction (IsolationLevel iso, string transactionName)
164 if (State == ConnectionState.Closed)
165 throw new InvalidOperationException ("The connection is not open.");
166 if (Transaction != null)
167 throw new InvalidOperationException ("SybaseConnection does not support parallel transactions.");
169 string isolevel = String.Empty;
171 case IsolationLevel.Chaos:
174 case IsolationLevel.ReadCommitted:
175 isolevel = "READ COMMITTED";
177 case IsolationLevel.ReadUncommitted:
178 isolevel = "READ UNCOMMITTED";
180 case IsolationLevel.RepeatableRead:
181 isolevel = "REPEATABLE READ";
183 case IsolationLevel.Serializable:
184 isolevel = "SERIALIZABLE";
188 tds.Execute (String.Format ("SET TRANSACTION ISOLATION LEVEL {0}\nBEGIN TRANSACTION {1}", isolevel, transactionName));
189 transaction = new SybaseTransaction (this, iso);
193 public void ChangeDatabase (string database)
195 if (!IsValidDatabaseName (database))
196 throw new ArgumentException (String.Format ("The database name {0} is not valid."));
197 if (State != ConnectionState.Open)
198 throw new InvalidOperationException ("The connection is not open");
199 tds.Execute (String.Format ("use {0}", database));
202 private void ChangeState (ConnectionState currentState)
204 ConnectionState originalState = state;
205 state = currentState;
206 OnStateChange (CreateStateChangeEvent (originalState, currentState));
211 if (Transaction != null && Transaction.IsOpen)
212 Transaction.Rollback ();
214 pool.ReleaseConnection (tds);
217 tds.TdsErrorMessage -= new TdsInternalErrorMessageEventHandler (ErrorHandler);
218 tds.TdsInfoMessage -= new TdsInternalInfoMessageEventHandler (MessageHandler);
219 ChangeState (ConnectionState.Closed);
222 public SybaseCommand CreateCommand ()
224 SybaseCommand command = new SybaseCommand ();
225 command.Connection = this;
229 private StateChangeEventArgs CreateStateChangeEvent (ConnectionState originalState, ConnectionState currentState)
231 return new StateChangeEventArgs (originalState, currentState);
234 private SybaseInfoMessageEventArgs CreateSybaseInfoMessageEvent (TdsInternalErrorCollection errors)
236 return new SybaseInfoMessageEventArgs (errors);
239 protected override void Dispose (bool disposing)
243 if (State == ConnectionState.Open)
248 base.Dispose (disposing);
254 public void EnlistDistributedTransaction (ITransaction transaction)
256 throw new NotImplementedException ();
259 object ICloneable.Clone ()
261 return new SybaseConnection (ConnectionString);
264 IDbTransaction IDbConnection.BeginTransaction ()
266 return BeginTransaction ();
269 IDbTransaction IDbConnection.BeginTransaction (IsolationLevel iso)
271 return BeginTransaction (iso);
274 IDbCommand IDbConnection.CreateCommand ()
276 return CreateCommand ();
279 void IDisposable.Dispose ()
282 GC.SuppressFinalize (this);
285 [MonoTODO ("Figure out the Sybase way to reset the connection.")]
288 string serverName = "";
289 if (connectionString == null)
290 throw new InvalidOperationException ("Connection string has not been initialized.");
294 ParseDataSource (dataSource, out port, out serverName);
295 tds = new Tds50 (serverName, port, PacketSize, ConnectionTimeout);
298 pool = (SybaseConnectionPool) SybaseConnectionPools [connectionString];
300 ParseDataSource (dataSource, out port, out serverName);
301 pool = new SybaseConnectionPool (serverName, port, packetSize, ConnectionTimeout, minPoolSize, maxPoolSize);
302 SybaseConnectionPools [connectionString] = pool;
304 tds = pool.AllocateConnection ();
307 catch (TdsTimeoutException e) {
308 throw SybaseException.FromTdsInternalException ((TdsInternalException) e);
311 tds.TdsErrorMessage += new TdsInternalErrorMessageEventHandler (ErrorHandler);
312 tds.TdsInfoMessage += new TdsInternalInfoMessageEventHandler (MessageHandler);
314 if (!tds.IsConnected) {
316 ChangeState (ConnectionState.Open);
317 ChangeDatabase (parms.Database);
319 else if (connectionReset) {
320 // tds.ExecuteNonQuery ("EXEC sp_reset_connection"); FIXME
321 ChangeState (ConnectionState.Open);
325 private void ParseDataSource (string theDataSource, out int thePort, out string theServerName)
328 thePort = 1433; // default TCP port for SQL Server
331 if ((idx = theDataSource.IndexOf (",")) > -1) {
\r
332 theServerName = theDataSource.Substring (0, idx);
333 string p = theDataSource.Substring (idx + 1);
334 thePort = Int32.Parse (p);
337 theServerName = theDataSource;
341 void SetConnectionString (string connectionString)
343 connectionString += ";";
344 NameValueCollection parameters = new NameValueCollection ();
346 if (connectionString == String.Empty)
349 bool inQuote = false;
350 bool inDQuote = false;
352 string name = String.Empty;
353 string value = String.Empty;
354 StringBuilder sb = new StringBuilder ();
356 foreach (char c in connectionString)
363 inDQuote = !inDQuote;
366 if (!inDQuote && !inQuote) {
367 if (name != String.Empty && name != null) {
368 value = sb.ToString ();
369 parameters [name.ToUpper ().Trim ()] = value.Trim ();
372 value = String.Empty;
373 sb = new StringBuilder ();
379 if (!inDQuote && !inQuote) {
380 name = sb.ToString ();
381 sb = new StringBuilder ();
392 if (this.ConnectionString == null)
394 SetDefaultConnectionParameters (parameters);
397 SetProperties (parameters);
399 this.connectionString = connectionString;
402 void SetDefaultConnectionParameters (NameValueCollection parameters)
404 if (null == parameters.Get ("APPLICATION NAME"))
405 parameters["APPLICATION NAME"] = ".Net SybaseClient Data Provider";
406 if (null == parameters.Get ("CONNECT TIMEOUT") && null == parameters.Get ("CONNECTION TIMEOUT"))
407 parameters["CONNECT TIMEOUT"] = "15";
408 if (null == parameters.Get ("CONNECTION LIFETIME"))
409 parameters["CONNECTION LIFETIME"] = "0";
410 if (null == parameters.Get ("CONNECTION RESET"))
411 parameters["CONNECTION RESET"] = "true";
412 if (null == parameters.Get ("ENLIST"))
413 parameters["ENLIST"] = "true";
414 if (null == parameters.Get ("INTEGRATED SECURITY") && null == parameters.Get ("TRUSTED_CONNECTION"))
415 parameters["INTEGRATED SECURITY"] = "false";
416 if (null == parameters.Get ("MAX POOL SIZE"))
417 parameters["MAX POOL SIZE"] = "100";
418 if (null == parameters.Get ("MIN POOL SIZE"))
419 parameters["MIN POOL SIZE"] = "0";
420 if (null == parameters.Get ("NETWORK LIBRARY") && null == parameters.Get ("NET"))
421 parameters["NETWORK LIBRARY"] = "dbmssocn";
422 if (null == parameters.Get ("PACKET SIZE"))
423 parameters["PACKET SIZE"] = "512";
424 if (null == parameters.Get ("PERSIST SECURITY INFO"))
425 parameters["PERSIST SECURITY INFO"] = "false";
426 if (null == parameters.Get ("POOLING"))
427 parameters["POOLING"] = "true";
428 if (null == parameters.Get ("WORKSTATION ID"))
429 parameters["WORKSTATION ID"] = Dns.GetHostByName ("localhost").HostName;
432 private void SetProperties (NameValueCollection parameters)
435 foreach (string name in parameters) {
436 value = parameters[name];
439 case "APPLICATION NAME" :
440 parms.ApplicationName = value;
442 case "ATTACHDBFILENAME" :
443 case "EXTENDED PROPERTIES" :
444 case "INITIAL FILE NAME" :
446 case "CONNECT TIMEOUT" :
447 case "CONNECTION TIMEOUT" :
448 connectionTimeout = Int32.Parse (value);
450 case "CONNECTION LIFETIME" :
452 case "CONNECTION RESET" :
453 connectionReset = !(value.ToUpper ().Equals ("FALSE") || value.ToUpper ().Equals ("NO"));
455 case "CURRENT LANGUAGE" :
456 parms.Language = value;
462 case "NETWORK ADDRESS" :
467 case "INITIAL CATALOG" :
469 parms.Database = value;
471 case "INTEGRATED SECURITY" :
472 case "TRUSTED_CONNECTION" :
474 case "MAX POOL SIZE" :
475 maxPoolSize = Int32.Parse (value);
477 case "MIN POOL SIZE" :
478 minPoolSize = Int32.Parse (value);
481 case "NETWORK LIBRARY" :
482 if (!value.ToUpper ().Equals ("DBMSSOCN"))
483 throw new ArgumentException ("Unsupported network library.");
486 packetSize = Int32.Parse (value);
490 parms.Password = value;
492 case "PERSIST SECURITY INFO" :
495 pooling = !(value.ToUpper ().Equals ("FALSE") || value.ToUpper ().Equals ("NO"));
500 case "WORKSTATION ID" :
501 parms.Hostname = value;
508 static bool IsValidDatabaseName (string database)
510 if (database.Length > 32 || database.Length < 1)
513 if (database[0] == '"' && database[database.Length] == '"')
514 database = database.Substring (1, database.Length - 2);
515 else if (Char.IsDigit (database[0]))
518 if (database[0] == '_')
521 foreach (char c in database.Substring (1, database.Length - 1))
522 if (!Char.IsLetterOrDigit (c) && c != '_')
527 private void OnSybaseInfoMessage (SybaseInfoMessageEventArgs value)
529 if (InfoMessage != null)
530 InfoMessage (this, value);
533 private void OnStateChange (StateChangeEventArgs value)
535 if (StateChange != null)
536 StateChange (this, value);
539 #endregion // Methods