* roottypes.cs: Rename from tree.cs.
[mono.git] / mcs / class / System.Data / System.Data.Odbc / OdbcConnection.cs
1 //
2 // System.Data.Odbc.OdbcConnection
3 //
4 // Authors:
5 //  Brian Ritchie (brianlritchie@hotmail.com) 
6 //
7 // Copyright (C) Brian Ritchie, 2002
8 //
9
10 //
11 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
12 //
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:
20 // 
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 // 
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.
31 //
32
33 using System.ComponentModel;
34 using System.Data;
35 using System.Data.Common;
36 using System.Runtime.InteropServices;
37 using System.EnterpriseServices;
38
39 namespace System.Data.Odbc
40 {
41         [DefaultEvent("InfoMessage")]
42 #if NET_2_0
43         public sealed class OdbcConnection : DbConnection, ICloneable
44 #else
45         public sealed class OdbcConnection : Component, ICloneable, IDbConnection
46 #endif //NET_2_0
47         {
48                 #region Fields
49
50                 string connectionString;
51                 int connectionTimeout;
52                 internal OdbcTransaction transaction;
53                 IntPtr henv=IntPtr.Zero, hdbc=IntPtr.Zero;
54                 bool disposed = false;                  
55                 
56                 #endregion
57
58                 #region Constructors
59                 
60                 public OdbcConnection () : this (String.Empty)
61                 {
62                 }
63
64                 public OdbcConnection (string connectionString)
65                 {
66                         Init (connectionString);
67                 }
68
69                 private void Init (string connectionString)
70                 {
71                         connectionTimeout = 15;
72                         ConnectionString = connectionString;
73                 }
74
75                 #endregion // Constructors
76
77                 #region Properties
78
79                 internal IntPtr hDbc
80                 {
81                         get { return hdbc; }
82                 }
83
84                 [OdbcCategoryAttribute ("DataCategory_Data")]           
85                 [DefaultValue ("")]
86                 [OdbcDescriptionAttribute ("Information used to connect to a Data Source")]     
87                 [RefreshPropertiesAttribute (RefreshProperties.All)]
88                 [EditorAttribute ("Microsoft.VSDesigner.Data.Odbc.Design.OdbcConnectionStringEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]
89                 [RecommendedAsConfigurableAttribute (true)]
90                 public 
91 #if NET_2_0
92                 override
93 #endif
94                 string ConnectionString {
95                         get {
96                                 return connectionString;
97                         }
98                         set {
99                                 connectionString = value;
100                         }
101                 }
102                 
103                 [OdbcDescriptionAttribute ("Current connection timeout value, not settable  in the ConnectionString")]
104                 [DefaultValue (15)]     
105                 public
106 #if NET_2_0
107                 new
108 #endif // NET_2_0
109                 int ConnectionTimeout {
110                         get {
111                                 return connectionTimeout;
112                         }
113                         set {
114                                 if (value < 0) {
115                                         throw new ArgumentException("Timout should not be less than zero.");
116                                 }
117                                 connectionTimeout = value;
118                         }
119                 }
120
121                 [DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Hidden)]
122                 [OdbcDescriptionAttribute ("Current data source Catlog value, 'Database=X' in the ConnectionString")]
123                 public
124 #if NET_2_0
125                 override
126 #endif // NET_2_0
127                 string Database {
128                         get {
129                                 return GetInfo (OdbcInfo.DatabaseName);
130                         }
131                 }
132
133                 [DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Hidden)]
134                 [OdbcDescriptionAttribute ("The ConnectionState indicating whether the connection is open or closed")]
135                 [BrowsableAttribute (false)]            
136                 public
137 #if NET_2_0
138                 override
139 #endif // NET_2_0
140                 ConnectionState State
141                 {
142                         get {
143                                 if (hdbc!=IntPtr.Zero) {
144                                         return ConnectionState.Open;
145                                 }
146                                 else
147                                         return ConnectionState.Closed;
148                         }
149                 }
150
151                 [MonoTODO]
152                 [DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Hidden)]
153                 [OdbcDescriptionAttribute ("Current data source, 'Server=X' in the ConnectionString")]
154                 public
155 #if NET_2_0
156                 override
157 #endif // NET_2_0
158                 string DataSource {
159                         get {
160                                 return GetInfo (OdbcInfo.DataSourceName);
161                         }
162                 }
163
164                 [MonoTODO]
165                 [DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Hidden)]
166                 [OdbcDescriptionAttribute ("Current ODBC Driver")]
167                 public string Driver {
168                         get {
169                                 return GetInfo (OdbcInfo.DriverName);
170                         }
171                 }
172                 
173                 [MonoTODO]
174                 [DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Hidden)]
175                 [OdbcDescriptionAttribute ("Version of the product accessed by the ODBC Driver")]
176                 [BrowsableAttribute (false)]
177                 public
178 #if NET_2_0
179                 override
180 #endif // NET_2_0
181                 string ServerVersion {
182                         get {
183                                 return GetInfo (OdbcInfo.DbmsVersion);
184                         }
185                 }
186
187                 
188                 #endregion // Properties
189         
190                 #region Methods
191         
192                 public
193 #if NET_2_0
194                 new
195 #endif // NET_2_0
196                 OdbcTransaction BeginTransaction ()
197                 {
198                         return BeginTransaction(IsolationLevel.Unspecified);
199                 }
200
201 #if ONLY_1_1              
202                 IDbTransaction IDbConnection.BeginTransaction ()
203                 {
204                         return (IDbTransaction) BeginTransaction();
205                 }
206 #endif // ONLY_1_1
207 #if NET_2_0
208                 protected override DbTransaction BeginDbTransaction (IsolationLevel level)
209                 {
210                         return BeginTransaction (level);
211                 }
212 #endif
213                 
214                 public
215 #if NET_2_0
216                 new
217 #endif // NET_2_0
218                 OdbcTransaction BeginTransaction (IsolationLevel level)
219                 {
220                         if (transaction==null)
221                         {
222                                 transaction=new OdbcTransaction(this,level);
223                                 return transaction;
224                         }
225                         else
226                                 throw new InvalidOperationException();
227                 }
228
229 #if ONLY_1_1
230                 IDbTransaction IDbConnection.BeginTransaction (IsolationLevel level)
231                 {
232                         return (IDbTransaction) BeginTransaction(level);
233                 }
234 #endif // ONLY_1_1
235
236                 public
237 #if NET_2_0
238                 override
239 #endif // NET_2_0
240                 void Close ()
241                 {
242                         OdbcReturn ret = OdbcReturn.Error;
243                         if (State == ConnectionState.Open) {
244                                 // disconnect
245                                 ret = libodbc.SQLDisconnect (hdbc);
246                                 if ( (ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo)) 
247                                         throw new OdbcException (new OdbcError ("SQLDisconnect", OdbcHandleType.Dbc,hdbc));
248
249                                 FreeHandles ();
250
251                                 transaction = null;
252
253                                 RaiseStateChange (ConnectionState.Open, ConnectionState.Closed);
254                         }
255                 }
256
257                 public
258 #if NET_2_0
259                 new
260 #endif // NET_2_0
261                 OdbcCommand CreateCommand ()
262                 {
263                         return new OdbcCommand("", this, transaction); 
264                 }
265
266                 [MonoTODO]
267                 public
268 #if NET_2_0
269                 override
270 #endif // NET_2_0
271                 void ChangeDatabase(string Database)
272                 {
273                         IntPtr ptr = IntPtr.Zero;
274                         OdbcReturn ret = OdbcReturn.Error;
275
276                         try {
277                                 ptr = Marshal.StringToHGlobalAnsi (Database);
278                                 ret = libodbc.SQLSetConnectAttr (hdbc, OdbcConnectionAttribute.CurrentCatalog, ptr, Database.Length);
279
280                                 if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
281                                         throw new OdbcException (new OdbcError ("SQLSetConnectAttr", OdbcHandleType.Dbc, hdbc));
282                         } finally {
283                                 if (ptr != IntPtr.Zero)
284                                         Marshal.FreeCoTaskMem (ptr);
285                         }
286                 }
287
288                 protected override void Dispose (bool disposing)
289                 {
290                         if (!this.disposed) {
291                                 try 
292                                 {
293                                         // release the native unmananged resources
294                                         this.Close();
295                                         this.disposed = true;
296                                 }
297                                 finally 
298                                 {
299                                         // call Dispose on the base class
300                                         base.Dispose(disposing);                        
301                                 }
302                         }
303                 }
304
305                 [MonoTODO]
306                 object ICloneable.Clone ()
307                 {
308                         throw new NotImplementedException();
309                 }
310
311 #if ONLY_1_1
312                 IDbCommand IDbConnection.CreateCommand ()
313                 {
314                         return (IDbCommand) CreateCommand ();
315                 }
316 #endif //ONLY_1_1
317
318 #if NET_2_0
319                 protected override DbCommand CreateDbCommand ()
320                 {
321                         return CreateCommand ();
322                 }
323 #endif
324
325                 public
326 #if NET_2_0
327                 override
328 #endif // NET_2_0
329                 void Open ()
330                 {
331                         if (State == ConnectionState.Open)
332                                 throw new InvalidOperationException ();
333
334                         OdbcReturn ret = OdbcReturn.Error;
335                 
336                         try {
337                                 // allocate Environment handle  
338                                 ret = libodbc.SQLAllocHandle (OdbcHandleType.Env, IntPtr.Zero, ref henv);
339                                 if ( (ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo)) 
340                                         throw new OdbcException (new OdbcError ("SQLAllocHandle"));
341                 
342                                 ret=libodbc.SQLSetEnvAttr (henv, OdbcEnv.OdbcVersion, (IntPtr) libodbc.SQL_OV_ODBC3 , 0); 
343                                 if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo)) 
344                                         throw new OdbcException (new OdbcError ("SQLSetEnvAttr", OdbcHandleType.Env,henv));
345                 
346                                 // allocate connection handle
347                                 ret=libodbc.SQLAllocHandle (OdbcHandleType.Dbc, henv, ref hdbc);
348                                 if ( (ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo)) 
349                                         throw new OdbcException (new OdbcError ("SQLAllocHandle",OdbcHandleType.Env,henv));
350                         
351                                 // DSN connection
352                                 if (ConnectionString.ToLower().IndexOf("dsn=")>=0)
353                                 {
354                                         string _uid="", _pwd="", _dsn="";
355                                         string[] items=ConnectionString.Split(new char[1]{';'});
356                                         foreach (string item in items)
357                                         {
358                                                 string[] parts=item.Split(new char[1] {'='});
359                                                 switch (parts[0].Trim().ToLower())
360                                                 {
361                                                 case "dsn":
362                                                         _dsn=parts[1].Trim();
363                                                         break;
364                                                 case "uid":
365                                                         _uid=parts[1].Trim();
366                                                         break;
367                                                 case "pwd":
368                                                         _pwd=parts[1].Trim();
369                                                         break;
370                                                 }
371                                         }
372                                         ret=libodbc.SQLConnect(hdbc, _dsn, -3, _uid, -3, _pwd, -3);
373                                         if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo)) 
374                                                 throw new OdbcException(new OdbcError("SQLConnect",OdbcHandleType.Dbc,hdbc));
375                                 }
376                                 else 
377                                 {
378                                         // DSN-less Connection
379                                         string OutConnectionString=new String(' ',1024);
380                                         short OutLen=0;
381                                         ret=libodbc.SQLDriverConnect(hdbc, IntPtr.Zero, ConnectionString, -3, 
382                                                                      OutConnectionString, (short) OutConnectionString.Length, ref OutLen, 0);
383                                         if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo)) 
384                                                 throw new OdbcException(new OdbcError("SQLDriverConnect",OdbcHandleType.Dbc,hdbc));
385                                 }
386
387                                 RaiseStateChange (ConnectionState.Closed, ConnectionState.Open);
388                         } catch (Exception) {
389                                 // free handles if any.
390                                 FreeHandles ();
391                                 throw;
392                         }
393                         disposed = false;
394                 }
395
396                 [MonoTODO]
397                 public static void ReleaseObjectPool ()
398                 {
399                         throw new NotImplementedException ();
400                 }
401
402                 private void FreeHandles ()
403                 {
404                         OdbcReturn ret = OdbcReturn.Error;
405                         if (hdbc != IntPtr.Zero) {
406                                 ret = libodbc.SQLFreeHandle ( (ushort) OdbcHandleType.Dbc, hdbc);       
407                                 if ( (ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo)) 
408                                         throw new OdbcException (new OdbcError ("SQLFreeHandle", OdbcHandleType.Dbc,hdbc));
409                         }
410                         hdbc = IntPtr.Zero;
411
412                         if (henv != IntPtr.Zero) {
413                                 ret = libodbc.SQLFreeHandle ( (ushort) OdbcHandleType.Env, henv);       
414                                 if ( (ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo)) 
415                                         throw new OdbcException (new OdbcError ("SQLFreeHandle", OdbcHandleType.Env,henv));
416                         }
417                         henv = IntPtr.Zero;
418                                 
419                 }
420                 
421
422                 [MonoTODO]
423                 public void EnlistDistributedTransaction ( ITransaction transaction) 
424                 {
425                 
426                         throw new NotImplementedException ();
427                 }
428
429                 internal string GetInfo (OdbcInfo info)
430                 {
431                         if (State == ConnectionState.Closed)
432                                 throw new InvalidOperationException ("The connection is closed.");
433                         
434                         OdbcReturn ret = OdbcReturn.Error;
435                         short max_length = 256;
436                         byte [] buffer = new byte [max_length];
437                         short actualLength = 0;
438                         
439                         ret = libodbc.SQLGetInfo (hdbc, info, buffer, max_length, ref actualLength);
440                         if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
441                                 throw new OdbcException (new OdbcError ("SQLGetInfo",
442                                                                         OdbcHandleType.Dbc,
443                                                                         hdbc));
444
445                         return System.Text.Encoding.Default.GetString (buffer);
446                 }
447
448                 private void RaiseStateChange (ConnectionState from, ConnectionState to)
449                 {
450 #if ONLY_1_1
451                         if (StateChange != null)
452                                 StateChange (this, new StateChangeEventArgs (from, to));
453 #else
454                         base.OnStateChange (new StateChangeEventArgs (from, to));
455 #endif
456                 }
457
458                 #endregion
459
460                 #region Events and Delegates
461
462 #if ONLY_1_1
463                 [OdbcDescription ("DbConnection_StateChange")]
464                 [OdbcCategory ("DataCategory_StateChange")]
465                 public event StateChangeEventHandler StateChange;
466 #endif // ONLY_1_1
467
468                 [OdbcDescription ("DbConnection_InfoMessage")]
469                 [OdbcCategory ("DataCategory_InfoMessage")]
470                 public event OdbcInfoMessageEventHandler InfoMessage;
471
472                 #endregion
473         }
474 }