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