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
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 using Mono
.Data
.Tds
.Protocol
;
35 using System
.Collections
;
36 using System
.Collections
.Specialized
;
37 using System
.ComponentModel
;
39 using System
.Data
.Common
;
40 using System
.EnterpriseServices
;
42 using System
.Net
.Sockets
;
45 namespace Mono
.Data
.SybaseClient
{
46 public sealed class SybaseConnection
: Component
, IDbConnection
, ICloneable
49 bool disposed
= false;
51 // The set of SQL connection pools
52 static TdsConnectionPoolManager sybaseConnectionPools
= new TdsConnectionPoolManager (TdsVersion
.tds50
);
54 // The current connection pool
55 TdsConnectionPool pool
;
57 // The connection string that identifies this connection
58 string connectionString
= null;
60 // The transaction object for the current transaction
61 SybaseTransaction transaction
= null;
63 // Connection parameters
64 TdsConnectionParameters parms
= new TdsConnectionParameters ();
68 int connectionTimeout
;
75 ConnectionState state
= ConnectionState
.Closed
;
77 SybaseDataReader dataReader
= null;
86 public SybaseConnection ()
91 public SybaseConnection (string connectionString
)
93 ConnectionString
= connectionString
;
96 #endregion // Constructors
100 public string ConnectionString
{
101 get { return connectionString; }
102 set { SetConnectionString (value); }
105 public int ConnectionTimeout
{
106 get { return connectionTimeout; }
109 public string Database
{
110 get { return tds.Database; }
113 internal SybaseDataReader DataReader
{
114 get { return dataReader; }
115 set { dataReader = value; }
118 public string DataSource
{
119 get { return dataSource; }
122 public int PacketSize
{
123 get { return packetSize; }
126 public string ServerVersion
{
127 get { return tds.ServerVersion; }
130 public ConnectionState State
{
131 get { return state; }
138 internal SybaseTransaction Transaction
{
139 get { return transaction; }
140 set { transaction = value; }
143 public string WorkstationId
{
144 get { return parms.Hostname; }
147 #endregion // Properties
149 #region Events and Delegates
151 public event SybaseInfoMessageEventHandler InfoMessage
;
152 public event StateChangeEventHandler StateChange
;
154 private void ErrorHandler (object sender
, TdsInternalErrorMessageEventArgs e
)
156 throw new SybaseException (e
.Class
, e
.LineNumber
, e
.Message
, e
.Number
, e
.Procedure
, e
.Server
, "Mono SybaseClient Data Provider", e
.State
);
159 private void MessageHandler (object sender
, TdsInternalInfoMessageEventArgs e
)
161 OnSybaseInfoMessage (CreateSybaseInfoMessageEvent (e
.Errors
));
164 #endregion // Events and Delegates
168 public SybaseTransaction
BeginTransaction ()
170 return BeginTransaction (IsolationLevel
.ReadCommitted
, String
.Empty
);
173 public SybaseTransaction
BeginTransaction (IsolationLevel iso
)
175 return BeginTransaction (iso
, String
.Empty
);
178 public SybaseTransaction
BeginTransaction (string transactionName
)
180 return BeginTransaction (IsolationLevel
.ReadCommitted
, transactionName
);
183 public SybaseTransaction
BeginTransaction (IsolationLevel iso
, string transactionName
)
185 if (State
== ConnectionState
.Closed
)
186 throw new InvalidOperationException ("The connection is not open.");
187 if (Transaction
!= null)
188 throw new InvalidOperationException ("SybaseConnection does not support parallel transactions.");
190 string isolevel
= String
.Empty
;
192 case IsolationLevel
.Chaos
:
195 case IsolationLevel
.ReadCommitted
:
196 isolevel
= "READ COMMITTED";
198 case IsolationLevel
.ReadUncommitted
:
199 isolevel
= "READ UNCOMMITTED";
201 case IsolationLevel
.RepeatableRead
:
202 isolevel
= "REPEATABLE READ";
204 case IsolationLevel
.Serializable
:
205 isolevel
= "SERIALIZABLE";
209 tds
.Execute (String
.Format ("SET TRANSACTION ISOLATION LEVEL {0}\nBEGIN TRANSACTION {1}", isolevel
, transactionName
));
210 transaction
= new SybaseTransaction (this, iso
);
214 public void ChangeDatabase (string database
)
216 if (!IsValidDatabaseName (database
))
217 throw new ArgumentException (String
.Format ("The database name {0} is not valid."));
218 if (State
!= ConnectionState
.Open
)
219 throw new InvalidOperationException ("The connection is not open");
220 tds
.Execute (String
.Format ("use {0}", database
));
223 private void ChangeState (ConnectionState currentState
)
225 ConnectionState originalState
= state
;
226 state
= currentState
;
227 OnStateChange (CreateStateChangeEvent (originalState
, currentState
));
232 if (Transaction
!= null && Transaction
.IsOpen
)
233 Transaction
.Rollback ();
235 pool
.ReleaseConnection (tds
);
238 tds
.TdsErrorMessage
-= new TdsInternalErrorMessageEventHandler (ErrorHandler
);
239 tds
.TdsInfoMessage
-= new TdsInternalInfoMessageEventHandler (MessageHandler
);
240 ChangeState (ConnectionState
.Closed
);
243 public SybaseCommand
CreateCommand ()
245 SybaseCommand command
= new SybaseCommand ();
246 command
.Connection
= this;
250 private StateChangeEventArgs
CreateStateChangeEvent (ConnectionState originalState
, ConnectionState currentState
)
252 return new StateChangeEventArgs (originalState
, currentState
);
255 private SybaseInfoMessageEventArgs
CreateSybaseInfoMessageEvent (TdsInternalErrorCollection errors
)
257 return new SybaseInfoMessageEventArgs (errors
);
260 protected override void Dispose (bool disposing
)
264 if (State
== ConnectionState
.Open
)
269 base.Dispose (disposing
);
275 public void EnlistDistributedTransaction (ITransaction transaction
)
277 throw new NotImplementedException ();
280 object ICloneable
.Clone ()
282 return new SybaseConnection (ConnectionString
);
285 IDbTransaction IDbConnection
.BeginTransaction ()
287 return BeginTransaction ();
290 IDbTransaction IDbConnection
.BeginTransaction (IsolationLevel iso
)
292 return BeginTransaction (iso
);
295 IDbCommand IDbConnection
.CreateCommand ()
297 return CreateCommand ();
300 void IDisposable
.Dispose ()
303 GC
.SuppressFinalize (this);
306 [MonoTODO ("Figure out the Sybase way to reset the connection.")]
309 string serverName
= "";
310 if (connectionString
== null || connectionString
.Equals (""))
311 throw new InvalidOperationException ("Connection string has not been initialized.");
315 ParseDataSource (dataSource
, out port
, out serverName
);
316 tds
= new Tds50 (serverName
, port
, PacketSize
, ConnectionTimeout
);
319 ParseDataSource (dataSource
, out port
, out serverName
);
320 TdsConnectionInfo info
= new TdsConnectionInfo (serverName
, port
, packetSize
, ConnectionTimeout
, minPoolSize
, maxPoolSize
);
321 pool
= sybaseConnectionPools
.GetConnectionPool (connectionString
, info
);
322 tds
= pool
.GetConnection ();
325 catch (TdsTimeoutException e
) {
326 throw SybaseException
.FromTdsInternalException ((TdsInternalException
) e
);
329 tds
.TdsErrorMessage
+= new TdsInternalErrorMessageEventHandler (ErrorHandler
);
330 tds
.TdsInfoMessage
+= new TdsInternalInfoMessageEventHandler (MessageHandler
);
332 if (!tds
.IsConnected
) {
335 ChangeState (ConnectionState
.Open
);
336 ChangeDatabase (parms
.Database
);
340 pool
.ReleaseConnection (tds
);
344 else if (connectionReset
) {
345 // tds.ExecuteNonQuery ("EXEC sp_reset_connection"); FIXME
346 ChangeState (ConnectionState
.Open
);
350 private void ParseDataSource (string theDataSource
, out int thePort
, out string theServerName
)
356 if ((idx
= theDataSource
.IndexOf (",")) > -1) {
357 theServerName
= theDataSource
.Substring (0, idx
);
358 string p
= theDataSource
.Substring (idx
+ 1);
359 thePort
= Int32
.Parse (p
);
362 theServerName
= theDataSource
;
366 private string ParseValue (string name
, string value)
368 if (name
.Length
== 0 && value.Length
> 0)
369 throw new ArgumentException ("Expected '=' delimiter while parsing connection value pair.");
371 return value.Trim ();
375 private void SetConnectionString (string connectionString
)
377 if (connectionString
== String
.Empty
) {
378 this.connectionString
= connectionString
;
382 NameValueCollection parameters
= new NameValueCollection ();
384 string name
= String
.Empty
;
385 string value = String
.Empty
;
386 StringBuilder sb
= new StringBuilder ();
388 char delimiter
= '\0';
390 foreach (char c
in connectionString
) {
394 if (delimiter
.Equals (c
))
396 else if (delimiter
.Equals ('\0'))
402 if (delimiter
.Equals ('\0')) {
403 value = ParseValue (name
, sb
.ToString ());
404 if (!value.Equals (""))
405 parameters
[name
.ToUpper ().Trim ()] = value;
407 sb
= new StringBuilder ();
413 if (delimiter
.Equals ('\0')) {
414 name
= sb
.ToString ();
415 sb
= new StringBuilder ();
426 if (!delimiter
.Equals ('\0'))
427 throw new ArgumentException (String
.Format ("Matching end delimiter {0} not found in connection option value.", delimiter
));
429 value = ParseValue (name
, sb
.ToString ());
430 if (!value.Equals (""))
431 parameters
[name
.ToUpper ().Trim ()] = value;
433 if (this.ConnectionString
== null)
434 SetDefaultConnectionParameters (parameters
);
436 SetProperties (parameters
);
438 this.connectionString
= connectionString
;
441 private void SetDefaultConnectionParameters (NameValueCollection parameters
)
443 if (null == parameters
.Get ("APPLICATION NAME"))
444 parameters
["APPLICATION NAME"] = "Mono SybaseClient Data Provider";
445 if (null == parameters
.Get ("CONNECT TIMEOUT") && null == parameters
.Get ("CONNECTION TIMEOUT"))
446 parameters
["CONNECT TIMEOUT"] = "15";
447 if (null == parameters
.Get ("CONNECTION LIFETIME"))
448 parameters
["CONNECTION LIFETIME"] = "0";
449 if (null == parameters
.Get ("CONNECTION RESET"))
450 parameters
["CONNECTION RESET"] = "true";
451 if (null == parameters
.Get ("ENLIST"))
452 parameters
["ENLIST"] = "true";
453 if (null == parameters
.Get ("INTEGRATED SECURITY") && null == parameters
.Get ("TRUSTED_CONNECTION"))
454 parameters
["INTEGRATED SECURITY"] = "false";
455 if (null == parameters
.Get ("MAX POOL SIZE"))
456 parameters
["MAX POOL SIZE"] = "100";
457 if (null == parameters
.Get ("MIN POOL SIZE"))
458 parameters
["MIN POOL SIZE"] = "0";
459 if (null == parameters
.Get ("NETWORK LIBRARY") && null == parameters
.Get ("NET"))
460 parameters
["NETWORK LIBRARY"] = "dbmssocn";
461 if (null == parameters
.Get ("PACKET SIZE"))
462 parameters
["PACKET SIZE"] = "512";
463 if (null == parameters
.Get ("PERSIST SECURITY INFO"))
464 parameters
["PERSIST SECURITY INFO"] = "false";
465 if (null == parameters
.Get ("POOLING"))
466 parameters
["POOLING"] = "true";
467 if (null == parameters
.Get ("WORKSTATION ID"))
468 parameters
["WORKSTATION ID"] = Dns
.GetHostByName ("localhost").HostName
;
471 private void SetProperties (NameValueCollection parameters
)
474 foreach (string name
in parameters
) {
475 value = parameters
[name
];
478 case "APPLICATION NAME" :
479 parms
.ApplicationName
= value;
481 case "ATTACHDBFILENAME" :
482 case "EXTENDED PROPERTIES" :
483 case "INITIAL FILE NAME" :
485 case "CONNECT TIMEOUT" :
486 case "CONNECTION TIMEOUT" :
487 connectionTimeout
= Int32
.Parse (value);
489 case "CONNECTION LIFETIME" :
491 case "CONNECTION RESET" :
492 connectionReset
= !(value.ToUpper ().Equals ("FALSE") || value.ToUpper ().Equals ("NO"));
494 case "CURRENT LANGUAGE" :
495 parms
.Language
= value;
501 case "NETWORK ADDRESS" :
506 case "INITIAL CATALOG" :
508 parms
.Database
= value;
510 case "INTEGRATED SECURITY" :
511 case "TRUSTED_CONNECTION" :
513 case "MAX POOL SIZE" :
514 maxPoolSize
= Int32
.Parse (value);
516 case "MIN POOL SIZE" :
517 minPoolSize
= Int32
.Parse (value);
520 case "NETWORK LIBRARY" :
521 if (!value.ToUpper ().Equals ("DBMSSOCN"))
522 throw new ArgumentException ("Unsupported network library.");
525 packetSize
= Int32
.Parse (value);
529 parms
.Password
= value;
531 case "PERSIST SECURITY INFO" :
534 pooling
= !(value.ToUpper ().Equals ("FALSE") || value.ToUpper ().Equals ("NO"));
539 case "WORKSTATION ID" :
540 parms
.Hostname
= value;
546 private static bool IsValidDatabaseName (string database
)
548 if (database
.Length
> 32 || database
.Length
< 1)
551 if (database
[0] == '"' && database
[database
.Length
] == '"')
552 database
= database
.Substring (1, database
.Length
- 2);
553 else if (Char
.IsDigit (database
[0]))
556 if (database
[0] == '_')
559 foreach (char c
in database
.Substring (1, database
.Length
- 1))
560 if (!Char
.IsLetterOrDigit (c
) && c
!= '_')
565 private void OnSybaseInfoMessage (SybaseInfoMessageEventArgs
value)
567 if (InfoMessage
!= null)
568 InfoMessage (this, value);
571 private void OnStateChange (StateChangeEventArgs
value)
573 if (StateChange
!= null)
574 StateChange (this, value);
577 #endregion // Methods