2002-10-25 Tim Coleman (tim@tmicoleman.com)
[mono.git] / mcs / class / Mono.Data.TdsClient / Mono.Data.TdsClient / TdsConnection.cs
1 //
2 // Mono.Data.TdsClient.TdsConnection.cs
3 //
4 // Author:
5 //   Tim Coleman (tim@timcoleman.com)
6 //
7 // Copyright (C) 2002 Tim Coleman
8 //
9
10 using Mono.Data.TdsClient.Internal;
11 using System;
12 using System.Collections;
13 using System.Collections.Specialized;
14 using System.ComponentModel;
15 using System.Data;
16 using System.Net;
17 using System.Text;
18
19 namespace Mono.Data.TdsClient {
20         public class TdsConnection : Component, ICloneable, IDbConnection
21         {
22                 #region Fields
23
24                 public static readonly string LibraryName = "Mono.Data.TdsClient";
25
26                 string dataSource;    // the database server name
27                 int port;             // which port to use
28                 int packetSize = 512; // what size are the packets we send/receive?
29                 bool connectionReset;
30                 bool pooling;
31                 int minPoolSize;
32                 int maxPoolSize;
33
34                 string connectionString = null;
35
36                 int connectionTimeout = 15;
37                 IsolationLevel isolationLevel = IsolationLevel.ReadCommitted;
38
39                 ConnectionState state = ConnectionState.Closed;
40                 TdsConnectionParameters parms = new TdsConnectionParameters ();
41                 TdsTransaction transaction = null;
42
43                 // This is the collection of connection pools available
44                 static Hashtable pools = new Hashtable ();
45                 TdsConnectionPool pool;
46
47                 // Our TDS object, the real workhorse
48                 ITds tds = null;
49
50                 #endregion // Fields
51
52                 #region Constructors
53
54                 public TdsConnection ()
55                         : this (String.Empty, 1433)
56                 {
57                 }
58
59                 public TdsConnection (string connectionString)
60                         : this (connectionString, 1433)
61                 {
62                 }
63
64                 public TdsConnection (string connectionString, int port)
65                 {
66                         this.port = port;
67                         parms.User = null;
68                         parms.Password = null;
69                         parms.LibraryName = LibraryName;
70                         SetConnectionString (connectionString);
71                 }
72                         
73                 #endregion // Constructors
74
75                 #region Properties
76
77                 public string ConnectionString {
78                         get { return connectionString; }
79                         set { SetConnectionString (value); }
80                 }
81
82                 public int ConnectionTimeout {
83                         get { return connectionTimeout; }
84                 }
85
86                 public string Database {
87                         get { return tds.Database; }
88                 }
89
90                 public string DataSource {
91                         get { return DataSource; }
92                 }
93
94                 public int PacketSize {
95                         get { return PacketSize; }
96                 }
97
98                 public string ServerVersion {
99                         get {
100                                 if (state == ConnectionState.Closed)
101                                         throw new InvalidOperationException ();
102                                 return tds.ServerVersion; 
103                         }
104                 }
105
106                 public ConnectionState State {
107                         get { return state; }
108                 }
109                 
110                 internal ITds Tds {
111                         get { return tds; }
112                 }
113         
114                 public string WorkstationId {
115                         get { return parms.Hostname; }
116                 }
117
118                 #endregion // Properties
119
120                 #region Methods
121
122                 public TdsTransaction BeginTransaction ()
123                 {
124                         return BeginTransaction (IsolationLevel.ReadCommitted);
125                 }
126
127                 public TdsTransaction BeginTransaction (IsolationLevel il)
128                 {
129                         if (state == ConnectionState.Closed)
130                                 throw new InvalidOperationException ("Invalid operation. The connection is closed.");
131                         if (transaction != null && transaction.Open)
132                                 throw new InvalidOperationException ("TdsConnection does not support parallel transactions.");
133
134                         transaction = new TdsTransaction (this, il);
135                         return transaction;
136                 }
137
138                 public void ChangeDatabase (string databaseName)
139                 {
140                         if (Database == databaseName)
141                                 return;
142                         tds.ExecuteNonQuery (String.Format ("use {0}", databaseName));
143                 }
144
145                 public void Close ()
146                 {
147                         // rollback any open transactions
148                         if (transaction != null && transaction.Open)
149                                 transaction.Rollback ();
150
151                         // if we aren't pooling, just close the connection
152                         // otherwise, relinquish the lock that we established in
153                         // the connection pool.
154                         if (pooling)
155                                 pool.ReleaseConnection (tds);
156                         else
157                                 tds.Disconnect ();
158
159                         this.state = ConnectionState.Closed;
160                 }
161
162                 [MonoTODO]
163                 protected override void Dispose (bool disposing)
164                 {
165                         Close ();
166                 }
167
168                 public TdsCommand CreateCommand ()
169                 {
170                         return (new TdsCommand (null, this, transaction));
171                 }
172
173                 object ICloneable.Clone()
174                 {
175                         throw new NotImplementedException ();
176                 }
177
178                 IDbTransaction IDbConnection.BeginTransaction ()
179                 {
180                         return BeginTransaction ();
181                 }
182
183                 IDbTransaction IDbConnection.BeginTransaction (IsolationLevel il)
184                 {
185                         return BeginTransaction (il);
186                 }
187
188                 IDbCommand IDbConnection.CreateCommand ()
189                 {
190                         return CreateCommand ();
191                 }
192
193                 public void Open ()
194                 {
195                         if (connectionString == null)
196                                 throw new InvalidOperationException ("The ConnectionString property has not been initialized.");
197                         if (parms.User == null)
198                                 throw new ArgumentException ("User name is null.");
199                         if (parms.Password == null)
200                                 throw new ArgumentException ("Password is null.  This may be a bug with blank passwords.");
201
202                         if (!pooling)
203                                 tds = new Tds42 (dataSource, port, packetSize);
204                         else {
205                                 pool = (TdsConnectionPool) pools[connectionString];
206                                 if (pool == null) {
207                                         lock (pools) {
208                                                 pool = new TdsConnectionPool (dataSource, port, packetSize, minPoolSize, maxPoolSize);
209                                                 pools[connectionString] = pool;
210                                         }
211                                 }
212                                 tds = pool.AllocateConnection ();
213                         }
214
215                         if (!tds.IsConnected) {
216                                 tds.Connect (parms);
217                                 ChangeDatabase (parms.Database);
218                         }
219                         this.state = ConnectionState.Open;
220                 }
221
222                 [MonoTODO]
223                 private void SetConnectionString (string connectionString)
224                 {
225                         connectionString += ";";
226                         NameValueCollection parameters = new NameValueCollection ();
227
228                         if (connectionString == String.Empty)
229                                 return;
230
231                         bool inQuote = false;
232                         bool inDQuote = false;
233
234                         string name = String.Empty;
235                         string value = String.Empty;
236                         StringBuilder sb = new StringBuilder ();
237
238                         foreach (char c in connectionString)
239                         {
240                                 switch (c) {
241                                 case '\'':
242                                         inQuote = !inQuote;
243                                         break;
244                                 case '"' :
245                                         inDQuote = !inDQuote;
246                                         break;
247                                 case ';' :
248                                         if (!inDQuote && !inQuote) {
249                                                 value = sb.ToString ();
250                                                 parameters [name.ToUpper ().Trim ()] = value.Trim ();
251                                                 name = String.Empty;
252                                                 value = String.Empty;
253                                                 sb = new StringBuilder ();
254                                         }
255                                         else
256                                                 sb.Append (c);
257                                         break;
258                                 case '=' :
259                                         if (!inDQuote && !inQuote) {
260                                                 name = sb.ToString ();
261                                                 sb = new StringBuilder ();
262                                         }
263                                         else
264                                                 sb.Append (c);
265                                         break;
266                                 default:
267                                         sb.Append (c);
268                                         break;
269                                 }
270                         }
271
272                         if (this.ConnectionString == null)
273                         {
274                                 SetDefaultConnectionParameters (parameters);
275                         }
276
277                         SetProperties (parameters);
278
279                         this.connectionString = connectionString;
280                 }
281
282
283                 private void SetDefaultConnectionParameters (NameValueCollection parameters)
284                 {
285                         if (null == parameters.Get ("APPLICATION NAME"))
286                                 parameters["APPLICATION NAME"] = ".Net SqlClient Data Provider";
287                         if (null == parameters.Get ("CONNECT TIMEOUT") && null == parameters.Get ("CONNECTION TIMEOUT"))
288                                 parameters["CONNECT TIMEOUT"] = "15";
289                         if (null == parameters.Get ("CONNECTION LIFETIME"))
290                                 parameters["CONNECTION LIFETIME"] = "0";
291                         if (null == parameters.Get ("CONNECTION RESET"))
292                                 parameters["CONNECTION RESET"] = "true";
293                         if (null == parameters.Get ("ENLIST"))
294                                 parameters["ENLIST"] = "true";
295                         if (null == parameters.Get ("INTEGRATED SECURITY") && null == parameters.Get ("TRUSTED_CONNECTION"))
296                                 parameters["INTEGRATED SECURITY"] = "false";
297                         if (null == parameters.Get ("MAX POOL SIZE"))
298                                 parameters["MAX POOL SIZE"] = "100";
299                         if (null == parameters.Get ("MIN POOL SIZE"))
300                                 parameters["MIN POOL SIZE"] = "0";
301                         if (null == parameters.Get ("NETWORK LIBRARY") && null == parameters.Get ("NET"))
302                                 parameters["NETWORK LIBRARY"] = "dbmssocn";
303                         if (null == parameters.Get ("PACKET SIZE"))
304                                 parameters["PACKET SIZE"] = "512";
305                         if (null == parameters.Get ("PERSIST SECURITY INFO"))
306                                 parameters["PERSIST SECURITY INFO"] = "false";
307                         if (null == parameters.Get ("POOLING"))
308                                 parameters["POOLING"] = "true";
309                         if (null == parameters.Get ("WORKSTATION ID"))
310                                 parameters["WORKSTATION ID"] = Dns.GetHostByName ("localhost").HostName;
311                 }
312
313                 private void SetProperties (NameValueCollection parameters)
314                 {
315                         string value;
316                         foreach (string name in parameters) {
317                                 value = parameters[name];
318
319                                 switch (name) {
320                                 case "APPLICATION NAME" :
321                                         parms.ApplicationName = value;
322                                         break;
323                                 case "ATTACHDBFILENAME" :
324                                 case "EXTENDED PROPERTIES" :
325                                 case "INITIAL FILE NAME" :
326                                         break;
327                                 case "CONNECT TIMEOUT" :
328                                 case "CONNECTION TIMEOUT" :
329                                         connectionTimeout = Int32.Parse (value);
330                                         break;
331                                 case "CONNECTION LIFETIME" :
332                                         break;
333                                 case "CONNECTION RESET" :
334                                         connectionReset = !(value.ToUpper ().Equals ("FALSE") || value.ToUpper ().Equals ("NO"));
335                                         break;
336                                 case "CURRENT LANGUAGE" :
337                                         parms.Language = value;
338                                         break;
339                                 case "DATA SOURCE" :
340                                 case "SERVER" :
341                                 case "ADDRESS" :
342                                 case "ADDR" :
343                                 case "NETWORK ADDRESS" :
344                                         dataSource = value;
345                                         break;
346                                 case "ENLIST" :
347                                         break;
348                                 case "INITIAL CATALOG" :
349                                 case "DATABASE" :
350                                         parms.Database = value;
351                                         break;
352                                 case "INTEGRATED SECURITY" :
353                                 case "TRUSTED_CONNECTION" :
354                                         break;
355                                 case "MAX POOL SIZE" :
356                                         maxPoolSize = Int32.Parse (value);
357                                         break;
358                                 case "MIN POOL SIZE" :
359                                         minPoolSize = Int32.Parse (value);
360                                         break;
361                                 case "NET" :
362                                 case "NETWORK LIBRARY" :
363                                         if (!value.ToUpper ().Equals ("DBMSSOCN"))
364                                                 throw new TdsException ("Unsupported network library.");
365                                         break;
366                                 case "PACKET SIZE" :
367                                         packetSize = Int32.Parse (value);
368                                         break;
369                                 case "PASSWORD" :
370                                 case "PWD" :
371                                         parms.Password = value;
372                                         break;
373                                 case "PERSIST SECURITY INFO" :
374                                         break;
375                                 case "POOLING" :
376                                         pooling = !(value.ToUpper ().Equals ("FALSE") || value.ToUpper ().Equals ("NO"));
377                                         break;
378                                 case "USER ID" :
379                                         parms.User = value;
380                                         break;
381                                 case "WORKSTATION ID" :
382                                         parms.Hostname = value;
383                                         break;
384                                 }
385                         }
386                 }
387                 #endregion // Methods
388         }
389 }