2 // System.Data.Odbc.OdbcConnection
5 // Brian Ritchie (brianlritchie@hotmail.com)
7 // Copyright (C) Brian Ritchie, 2002
11 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
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 System.Collections;
34 using System.ComponentModel;
36 using System.Data.Common;
37 using System.EnterpriseServices;
38 using System.Runtime.InteropServices;
40 #if NET_2_0 && !TARGET_JVM
41 using System.Transactions;
44 namespace System.Data.Odbc
46 [DefaultEvent ("InfoMessage")]
48 public sealed class OdbcConnection : DbConnection, ICloneable
50 public sealed class OdbcConnection : Component, ICloneable, IDbConnection
55 string connectionString;
56 int connectionTimeout;
57 internal OdbcTransaction transaction;
58 IntPtr henv = IntPtr.Zero;
59 IntPtr hdbc = IntPtr.Zero;
61 ArrayList linkedCommands;
67 public OdbcConnection () : this (String.Empty)
71 public OdbcConnection (string connectionString)
73 connectionTimeout = 15;
74 ConnectionString = connectionString;
77 #endregion // Constructors
81 internal IntPtr hDbc {
85 [OdbcCategoryAttribute ("DataCategory_Data")]
87 [OdbcDescriptionAttribute ("Information used to connect to a Data Source")]
88 [RefreshPropertiesAttribute (RefreshProperties.All)]
89 [EditorAttribute ("Microsoft.VSDesigner.Data.Odbc.Design.OdbcConnectionStringEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]
90 [RecommendedAsConfigurableAttribute (true)]
95 string ConnectionString {
97 if (connectionString == null)
99 return connectionString;
101 set { connectionString = value; }
104 [OdbcDescriptionAttribute ("Current connection timeout value, not settable in the ConnectionString")]
107 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
113 int ConnectionTimeout {
115 return connectionTimeout;
119 throw new ArgumentException("Timout should not be less than zero.");
120 connectionTimeout = value;
124 [DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Hidden)]
125 [OdbcDescriptionAttribute ("Current data source Catlog value, 'Database=X' in the ConnectionString")]
132 if (State == ConnectionState.Closed)
134 return GetInfo (OdbcInfo.DatabaseName);
138 [DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Hidden)]
139 [OdbcDescriptionAttribute ("The ConnectionState indicating whether the connection is open or closed")]
140 [BrowsableAttribute (false)]
145 ConnectionState State {
147 if (hdbc != IntPtr.Zero)
148 return ConnectionState.Open;
149 return ConnectionState.Closed;
153 [DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Hidden)]
154 [OdbcDescriptionAttribute ("Current data source, 'Server=X' in the ConnectionString")]
164 if (State == ConnectionState.Closed)
166 return GetInfo (OdbcInfo.DataSourceName);
173 [DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Hidden)]
174 [OdbcDescriptionAttribute ("Current ODBC Driver")]
175 public string Driver {
177 if (State == ConnectionState.Closed)
180 return GetInfo (OdbcInfo.DriverName);
184 [DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Hidden)]
185 [OdbcDescriptionAttribute ("Version of the product accessed by the ODBC Driver")]
186 [BrowsableAttribute (false)]
191 string ServerVersion {
193 return GetInfo (OdbcInfo.DbmsVersion);
197 internal string SafeDriver {
199 string driver_name = GetSafeInfo (OdbcInfo.DriverName);
200 if (driver_name == null)
206 #endregion // Properties
214 OdbcTransaction BeginTransaction ()
216 return BeginTransaction (IsolationLevel.Unspecified);
220 IDbTransaction IDbConnection.BeginTransaction ()
222 return (IDbTransaction) BeginTransaction ();
227 protected override DbTransaction BeginDbTransaction (IsolationLevel isolationLevel)
229 return BeginTransaction (isolationLevel);
237 OdbcTransaction BeginTransaction (IsolationLevel isolevel)
239 if (State == ConnectionState.Closed)
240 throw ExceptionHelper.ConnectionClosed ();
242 if (transaction == null) {
243 transaction = new OdbcTransaction (this, isolevel);
246 throw new InvalidOperationException ();
250 IDbTransaction IDbConnection.BeginTransaction (IsolationLevel isolevel)
252 return (IDbTransaction) BeginTransaction (isolevel);
262 OdbcReturn ret = OdbcReturn.Error;
263 if (State == ConnectionState.Open) {
264 // close any associated commands
265 if (linkedCommands != null) {
266 for (int i = 0; i < linkedCommands.Count; i++) {
267 WeakReference wr = (WeakReference) linkedCommands [i];
270 OdbcCommand c = (OdbcCommand) wr.Target;
274 linkedCommands = null;
278 ret = libodbc.SQLDisconnect (hdbc);
279 if ((ret != OdbcReturn.Success) && (ret != OdbcReturn.SuccessWithInfo))
280 throw CreateOdbcException (OdbcHandleType.Dbc, hdbc);
284 RaiseStateChange (ConnectionState.Open, ConnectionState.Closed);
292 OdbcCommand CreateCommand ()
294 return new OdbcCommand (string.Empty, this, transaction);
301 void ChangeDatabase (string value)
303 IntPtr ptr = IntPtr.Zero;
304 OdbcReturn ret = OdbcReturn.Error;
307 ptr = Marshal.StringToHGlobalUni (value);
308 ret = libodbc.SQLSetConnectAttr (hdbc, OdbcConnectionAttribute.CurrentCatalog, ptr, value.Length * 2);
310 if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
311 throw CreateOdbcException (OdbcHandleType.Dbc, hdbc);
313 if (ptr != IntPtr.Zero)
314 Marshal.FreeCoTaskMem (ptr);
318 protected override void Dispose (bool disposing)
320 if (!this.disposed) {
322 // release the native unmananged resources
324 this.disposed = true;
326 // call Dispose on the base class
327 base.Dispose (disposing);
333 object ICloneable.Clone ()
335 throw new NotImplementedException ();
339 IDbCommand IDbConnection.CreateCommand ()
341 return (IDbCommand) CreateCommand ();
346 protected override DbCommand CreateDbCommand ()
348 return CreateCommand ();
358 if (State == ConnectionState.Open)
359 throw new InvalidOperationException ();
361 OdbcReturn ret = OdbcReturn.Error;
362 OdbcException e = null;
365 // allocate Environment handle
366 ret = libodbc.SQLAllocHandle (OdbcHandleType.Env, IntPtr.Zero, ref henv);
367 if ((ret != OdbcReturn.Success) && (ret != OdbcReturn.SuccessWithInfo)) {
368 OdbcErrorCollection errors = new OdbcErrorCollection ();
369 errors.Add (new OdbcError (this));
370 e = new OdbcException (errors);
375 ret = libodbc.SQLSetEnvAttr (henv, OdbcEnv.OdbcVersion, (IntPtr) libodbc.SQL_OV_ODBC3 , 0);
376 if ((ret != OdbcReturn.Success) && (ret != OdbcReturn.SuccessWithInfo))
377 throw CreateOdbcException (OdbcHandleType.Env, henv);
379 // allocate connection handle
380 ret = libodbc.SQLAllocHandle (OdbcHandleType.Dbc, henv, ref hdbc);
381 if ((ret != OdbcReturn.Success) && (ret != OdbcReturn.SuccessWithInfo))
382 throw CreateOdbcException (OdbcHandleType.Env, henv);
385 if (ConnectionString.ToLower ().IndexOf ("dsn=") >= 0) {
386 string _uid = string.Empty, _pwd = string.Empty, _dsn = string.Empty;
387 string [] items = ConnectionString.Split (new char[1] {';'});
388 foreach (string item in items)
390 string [] parts = item.Split (new char[1] {'='});
391 switch (parts [0].Trim ().ToLower ()) {
393 _dsn = parts [1].Trim ();
396 _uid = parts [1].Trim ();
399 _pwd = parts [1].Trim ();
403 ret = libodbc.SQLConnect(hdbc, _dsn, -3, _uid, -3, _pwd, -3);
404 if ((ret != OdbcReturn.Success) && (ret != OdbcReturn.SuccessWithInfo))
405 throw CreateOdbcException (OdbcHandleType.Dbc, hdbc);
407 // DSN-less Connection
408 string OutConnectionString = new String (' ',1024);
410 ret = libodbc.SQLDriverConnect (hdbc, IntPtr.Zero, ConnectionString, -3,
411 OutConnectionString, (short) OutConnectionString.Length, ref OutLen, 0);
412 if ((ret != OdbcReturn.Success) && (ret != OdbcReturn.SuccessWithInfo))
413 throw CreateOdbcException (OdbcHandleType.Dbc, hdbc);
416 RaiseStateChange (ConnectionState.Closed, ConnectionState.Open);
418 // free handles if any.
426 public static void ReleaseObjectPool ()
428 throw new NotImplementedException ();
431 private void FreeHandles ()
433 OdbcReturn ret = OdbcReturn.Error;
434 if (hdbc != IntPtr.Zero) {
435 ret = libodbc.SQLFreeHandle ((ushort) OdbcHandleType.Dbc, hdbc);
436 if ( (ret != OdbcReturn.Success) && (ret != OdbcReturn.SuccessWithInfo))
437 throw CreateOdbcException (OdbcHandleType.Dbc, hdbc);
441 if (henv != IntPtr.Zero) {
442 ret = libodbc.SQLFreeHandle ((ushort) OdbcHandleType.Env, henv);
443 if ( (ret != OdbcReturn.Success) && (ret != OdbcReturn.SuccessWithInfo))
444 throw CreateOdbcException (OdbcHandleType.Env, henv);
450 public override DataTable GetSchema ()
452 if (State == ConnectionState.Closed)
453 throw ExceptionHelper.ConnectionClosed ();
454 return MetaDataCollections.Instance;
457 public override DataTable GetSchema (string collectionName)
459 return GetSchema (collectionName, null);
462 public override DataTable GetSchema (string collectionName, string [] restrictionValues)
464 if (State == ConnectionState.Closed)
465 throw ExceptionHelper.ConnectionClosed ();
466 return GetSchema (collectionName, null);
470 public override void EnlistTransaction (Transaction transaction)
472 throw new NotImplementedException ();
477 public void EnlistDistributedTransaction (ITransaction transaction)
479 throw new NotImplementedException ();
482 internal string GetInfo (OdbcInfo info)
484 if (State == ConnectionState.Closed)
485 throw new InvalidOperationException ("The connection is closed.");
487 OdbcReturn ret = OdbcReturn.Error;
488 short max_length = 512;
489 byte [] buffer = new byte [512];
490 short actualLength = 0;
492 ret = libodbc.SQLGetInfo (hdbc, info, buffer, max_length, ref actualLength);
493 if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
494 throw CreateOdbcException (OdbcHandleType.Dbc, hdbc);
495 return Encoding.Unicode.GetString (buffer, 0, actualLength);
498 string GetSafeInfo (OdbcInfo info)
500 if (State == ConnectionState.Closed)
503 OdbcReturn ret = OdbcReturn.Error;
504 short max_length = 512;
505 byte [] buffer = new byte [512];
506 short actualLength = 0;
508 ret = libodbc.SQLGetInfo (hdbc, info, buffer, max_length, ref actualLength);
509 if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
511 return Encoding.Unicode.GetString (buffer, 0, actualLength);
514 private void RaiseStateChange (ConnectionState from, ConnectionState to)
517 if (StateChange != null)
518 StateChange (this, new StateChangeEventArgs (from, to));
520 base.OnStateChange (new StateChangeEventArgs (from, to));
524 private OdbcInfoMessageEventArgs CreateOdbcInfoMessageEvent (OdbcErrorCollection errors)
526 return new OdbcInfoMessageEventArgs (errors);
529 private void OnOdbcInfoMessage (OdbcInfoMessageEventArgs e)
531 if (InfoMessage != null)
532 InfoMessage (this, e);
535 internal OdbcException CreateOdbcException (OdbcHandleType HandleType, IntPtr Handle)
540 OdbcReturn ret = OdbcReturn.Success;
542 OdbcErrorCollection errors = new OdbcErrorCollection ();
545 byte [] buf_MsgText = new byte [buflen * 2];
546 byte [] buf_SqlState = new byte [buflen * 2];
548 switch (HandleType) {
549 case OdbcHandleType.Dbc:
550 ret = libodbc.SQLError (IntPtr.Zero, Handle, IntPtr.Zero, buf_SqlState,
551 ref nativeerror, buf_MsgText, buflen, ref txtlen);
553 case OdbcHandleType.Stmt:
554 ret = libodbc.SQLError (IntPtr.Zero, IntPtr.Zero, Handle, buf_SqlState,
555 ref nativeerror, buf_MsgText, buflen, ref txtlen);
557 case OdbcHandleType.Env:
558 ret = libodbc.SQLError (Handle, IntPtr.Zero, IntPtr.Zero, buf_SqlState,
559 ref nativeerror, buf_MsgText, buflen, ref txtlen);
563 if (ret != OdbcReturn.Success)
566 string state = RemoveTrailingNullChar (Encoding.Unicode.GetString (buf_SqlState));
567 string message = Encoding.Unicode.GetString (buf_MsgText, 0, txtlen * 2);
569 errors.Add (new OdbcError (message, state, nativeerror));
572 string source = SafeDriver;
573 foreach (OdbcError error in errors)
574 error.SetSource (source);
575 return new OdbcException (errors);
578 static string RemoveTrailingNullChar (string value)
580 return value.TrimEnd ('\0');
583 internal void Link (OdbcCommand cmd)
585 if (linkedCommands == null)
586 linkedCommands = new ArrayList ();
587 linkedCommands.Add (new WeakReference (cmd));
590 internal void Unlink (OdbcCommand cmd)
592 if (linkedCommands == null)
595 for (int i = 0; i < linkedCommands.Count; i++) {
596 WeakReference wr = (WeakReference) linkedCommands [i];
599 OdbcCommand c = (OdbcCommand) wr.Target;
601 linkedCommands [i] = null;
609 #region Events and Delegates
612 [OdbcDescription ("DbConnection_StateChange")]
613 [OdbcCategory ("DataCategory_StateChange")]
614 public event StateChangeEventHandler StateChange;
617 [OdbcDescription ("DbConnection_InfoMessage")]
618 [OdbcCategory ("DataCategory_InfoMessage")]
619 public event OdbcInfoMessageEventHandler InfoMessage;
621 private void MessageHandler (OdbcException e)
623 OnOdbcInfoMessage (CreateOdbcInfoMessageEvent (e.Errors));