OdbcConnection.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589
  1. //
  2. // System.Data.Odbc.OdbcConnection
  3. //
  4. // Authors:
  5. // Brian Ritchie ([email protected])
  6. //
  7. // Copyright (C) Brian Ritchie, 2002
  8. //
  9. //
  10. // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
  11. //
  12. // Permission is hereby granted, free of charge, to any person obtaining
  13. // a copy of this software and associated documentation files (the
  14. // "Software"), to deal in the Software without restriction, including
  15. // without limitation the rights to use, copy, modify, merge, publish,
  16. // distribute, sublicense, and/or sell copies of the Software, and to
  17. // permit persons to whom the Software is furnished to do so, subject to
  18. // the following conditions:
  19. //
  20. // The above copyright notice and this permission notice shall be
  21. // included in all copies or substantial portions of the Software.
  22. //
  23. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  24. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  25. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  26. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  27. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  28. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  29. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  30. //
  31. using System.ComponentModel;
  32. using System.Data;
  33. using System.Data.Common;
  34. using System.EnterpriseServices;
  35. using System.Runtime.InteropServices;
  36. using System.Text;
  37. #if NET_2_0 && !TARGET_JVM
  38. using System.Transactions;
  39. #endif
  40. namespace System.Data.Odbc
  41. {
  42. [DefaultEvent ("InfoMessage")]
  43. #if NET_2_0
  44. public sealed class OdbcConnection : DbConnection, ICloneable
  45. #else
  46. public sealed class OdbcConnection : Component, ICloneable, IDbConnection
  47. #endif //NET_2_0
  48. {
  49. #region Fields
  50. string connectionString;
  51. int connectionTimeout;
  52. internal OdbcTransaction transaction;
  53. IntPtr henv = IntPtr.Zero;
  54. IntPtr hdbc = IntPtr.Zero;
  55. bool disposed;
  56. #endregion
  57. #region Constructors
  58. public OdbcConnection () : this (String.Empty)
  59. {
  60. }
  61. public OdbcConnection (string connectionString)
  62. {
  63. connectionTimeout = 15;
  64. ConnectionString = connectionString;
  65. }
  66. #endregion // Constructors
  67. #region Properties
  68. internal IntPtr hDbc {
  69. get { return hdbc; }
  70. }
  71. [OdbcCategoryAttribute ("DataCategory_Data")]
  72. [DefaultValue ("")]
  73. [OdbcDescriptionAttribute ("Information used to connect to a Data Source")]
  74. [RefreshPropertiesAttribute (RefreshProperties.All)]
  75. [EditorAttribute ("Microsoft.VSDesigner.Data.Odbc.Design.OdbcConnectionStringEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]
  76. [RecommendedAsConfigurableAttribute (true)]
  77. public
  78. #if NET_2_0
  79. override
  80. #endif
  81. string ConnectionString {
  82. get {
  83. if (connectionString == null)
  84. return string.Empty;
  85. return connectionString;
  86. }
  87. set { connectionString = value; }
  88. }
  89. [OdbcDescriptionAttribute ("Current connection timeout value, not settable in the ConnectionString")]
  90. [DefaultValue (15)]
  91. #if NET_2_0
  92. [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
  93. #endif
  94. public
  95. #if NET_2_0
  96. new
  97. #endif // NET_2_0
  98. int ConnectionTimeout {
  99. get {
  100. return connectionTimeout;
  101. }
  102. set {
  103. if (value < 0)
  104. throw new ArgumentException("Timout should not be less than zero.");
  105. connectionTimeout = value;
  106. }
  107. }
  108. [DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Hidden)]
  109. [OdbcDescriptionAttribute ("Current data source Catlog value, 'Database=X' in the ConnectionString")]
  110. public
  111. #if NET_2_0
  112. override
  113. #endif // NET_2_0
  114. string Database {
  115. get {
  116. if (State == ConnectionState.Closed)
  117. return string.Empty;
  118. return GetInfo (OdbcInfo.DatabaseName);
  119. }
  120. }
  121. [DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Hidden)]
  122. [OdbcDescriptionAttribute ("The ConnectionState indicating whether the connection is open or closed")]
  123. [BrowsableAttribute (false)]
  124. public
  125. #if NET_2_0
  126. override
  127. #endif // NET_2_0
  128. ConnectionState State {
  129. get {
  130. if (hdbc != IntPtr.Zero)
  131. return ConnectionState.Open;
  132. return ConnectionState.Closed;
  133. }
  134. }
  135. [DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Hidden)]
  136. [OdbcDescriptionAttribute ("Current data source, 'Server=X' in the ConnectionString")]
  137. #if NET_2_0
  138. [Browsable (false)]
  139. #endif
  140. public
  141. #if NET_2_0
  142. override
  143. #endif // NET_2_0
  144. string DataSource {
  145. get {
  146. if (State == ConnectionState.Closed)
  147. return string.Empty;
  148. return GetInfo (OdbcInfo.DataSourceName);
  149. }
  150. }
  151. #if NET_2_0
  152. [Browsable (false)]
  153. #endif
  154. [DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Hidden)]
  155. [OdbcDescriptionAttribute ("Current ODBC Driver")]
  156. public string Driver {
  157. get {
  158. if (State == ConnectionState.Closed)
  159. return string.Empty;
  160. return GetInfo (OdbcInfo.DriverName);
  161. }
  162. }
  163. [DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Hidden)]
  164. [OdbcDescriptionAttribute ("Version of the product accessed by the ODBC Driver")]
  165. [BrowsableAttribute (false)]
  166. public
  167. #if NET_2_0
  168. override
  169. #endif // NET_2_0
  170. string ServerVersion {
  171. get {
  172. return GetInfo (OdbcInfo.DbmsVersion);
  173. }
  174. }
  175. internal string SafeDriver {
  176. get {
  177. string driver_name = GetSafeInfo (OdbcInfo.DriverName);
  178. if (driver_name == null)
  179. return string.Empty;
  180. return driver_name;
  181. }
  182. }
  183. #endregion // Properties
  184. #region Methods
  185. public
  186. #if NET_2_0
  187. new
  188. #endif // NET_2_0
  189. OdbcTransaction BeginTransaction ()
  190. {
  191. return BeginTransaction (IsolationLevel.Unspecified);
  192. }
  193. #if ONLY_1_1
  194. IDbTransaction IDbConnection.BeginTransaction ()
  195. {
  196. return (IDbTransaction) BeginTransaction ();
  197. }
  198. #endif // ONLY_1_1
  199. #if NET_2_0
  200. protected override DbTransaction BeginDbTransaction (IsolationLevel isolationLevel)
  201. {
  202. return BeginTransaction (isolationLevel);
  203. }
  204. #endif
  205. public
  206. #if NET_2_0
  207. new
  208. #endif // NET_2_0
  209. OdbcTransaction BeginTransaction (IsolationLevel isolevel)
  210. {
  211. if (State == ConnectionState.Closed)
  212. throw ExceptionHelper.ConnectionClosed ();
  213. if (transaction == null) {
  214. transaction = new OdbcTransaction (this, isolevel);
  215. return transaction;
  216. } else
  217. throw new InvalidOperationException ();
  218. }
  219. #if ONLY_1_1
  220. IDbTransaction IDbConnection.BeginTransaction (IsolationLevel isolevel)
  221. {
  222. return (IDbTransaction) BeginTransaction (isolevel);
  223. }
  224. #endif // ONLY_1_1
  225. public
  226. #if NET_2_0
  227. override
  228. #endif // NET_2_0
  229. void Close ()
  230. {
  231. OdbcReturn ret = OdbcReturn.Error;
  232. if (State == ConnectionState.Open) {
  233. // disconnect
  234. ret = libodbc.SQLDisconnect (hdbc);
  235. if ((ret != OdbcReturn.Success) && (ret != OdbcReturn.SuccessWithInfo))
  236. throw CreateOdbcException (OdbcHandleType.Dbc, hdbc);
  237. FreeHandles ();
  238. transaction = null;
  239. RaiseStateChange (ConnectionState.Open, ConnectionState.Closed);
  240. }
  241. }
  242. public
  243. #if NET_2_0
  244. new
  245. #endif // NET_2_0
  246. OdbcCommand CreateCommand ()
  247. {
  248. return new OdbcCommand (string.Empty, this, transaction);
  249. }
  250. public
  251. #if NET_2_0
  252. override
  253. #endif // NET_2_0
  254. void ChangeDatabase (string value)
  255. {
  256. IntPtr ptr = IntPtr.Zero;
  257. OdbcReturn ret = OdbcReturn.Error;
  258. try {
  259. ptr = Marshal.StringToHGlobalUni (value);
  260. ret = libodbc.SQLSetConnectAttr (hdbc, OdbcConnectionAttribute.CurrentCatalog, ptr, value.Length * 2);
  261. if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
  262. throw CreateOdbcException (OdbcHandleType.Dbc, hdbc);
  263. } finally {
  264. if (ptr != IntPtr.Zero)
  265. Marshal.FreeCoTaskMem (ptr);
  266. }
  267. }
  268. protected override void Dispose (bool disposing)
  269. {
  270. if (!this.disposed) {
  271. try {
  272. // release the native unmananged resources
  273. this.Close ();
  274. this.disposed = true;
  275. } finally {
  276. // call Dispose on the base class
  277. base.Dispose (disposing);
  278. }
  279. }
  280. }
  281. [MonoTODO]
  282. object ICloneable.Clone ()
  283. {
  284. throw new NotImplementedException ();
  285. }
  286. #if ONLY_1_1
  287. IDbCommand IDbConnection.CreateCommand ()
  288. {
  289. return (IDbCommand) CreateCommand ();
  290. }
  291. #endif //ONLY_1_1
  292. #if NET_2_0
  293. protected override DbCommand CreateDbCommand ()
  294. {
  295. return CreateCommand ();
  296. }
  297. #endif
  298. public
  299. #if NET_2_0
  300. override
  301. #endif // NET_2_0
  302. void Open ()
  303. {
  304. if (State == ConnectionState.Open)
  305. throw new InvalidOperationException ();
  306. OdbcReturn ret = OdbcReturn.Error;
  307. OdbcException e = null;
  308. try {
  309. // allocate Environment handle
  310. ret = libodbc.SQLAllocHandle (OdbcHandleType.Env, IntPtr.Zero, ref henv);
  311. if ((ret != OdbcReturn.Success) && (ret != OdbcReturn.SuccessWithInfo)) {
  312. OdbcErrorCollection errors = new OdbcErrorCollection ();
  313. errors.Add (new OdbcError (this));
  314. e = new OdbcException (errors);
  315. MessageHandler (e);
  316. throw e;
  317. }
  318. ret = libodbc.SQLSetEnvAttr (henv, OdbcEnv.OdbcVersion, (IntPtr) libodbc.SQL_OV_ODBC3 , 0);
  319. if ((ret != OdbcReturn.Success) && (ret != OdbcReturn.SuccessWithInfo))
  320. throw CreateOdbcException (OdbcHandleType.Env, henv);
  321. // allocate connection handle
  322. ret = libodbc.SQLAllocHandle (OdbcHandleType.Dbc, henv, ref hdbc);
  323. if ((ret != OdbcReturn.Success) && (ret != OdbcReturn.SuccessWithInfo))
  324. throw CreateOdbcException (OdbcHandleType.Env, henv);
  325. // DSN connection
  326. if (ConnectionString.ToLower ().IndexOf ("dsn=") >= 0) {
  327. string _uid = string.Empty, _pwd = string.Empty, _dsn = string.Empty;
  328. string [] items = ConnectionString.Split (new char[1] {';'});
  329. foreach (string item in items)
  330. {
  331. string [] parts = item.Split (new char[1] {'='});
  332. switch (parts [0].Trim ().ToLower ()) {
  333. case "dsn":
  334. _dsn = parts [1].Trim ();
  335. break;
  336. case "uid":
  337. _uid = parts [1].Trim ();
  338. break;
  339. case "pwd":
  340. _pwd = parts [1].Trim ();
  341. break;
  342. }
  343. }
  344. ret = libodbc.SQLConnect(hdbc, _dsn, -3, _uid, -3, _pwd, -3);
  345. if ((ret != OdbcReturn.Success) && (ret != OdbcReturn.SuccessWithInfo))
  346. throw CreateOdbcException (OdbcHandleType.Dbc, hdbc);
  347. } else {
  348. // DSN-less Connection
  349. string OutConnectionString = new String (' ',1024);
  350. short OutLen = 0;
  351. ret = libodbc.SQLDriverConnect (hdbc, IntPtr.Zero, ConnectionString, -3,
  352. OutConnectionString, (short) OutConnectionString.Length, ref OutLen, 0);
  353. if ((ret != OdbcReturn.Success) && (ret != OdbcReturn.SuccessWithInfo))
  354. throw CreateOdbcException (OdbcHandleType.Dbc, hdbc);
  355. }
  356. RaiseStateChange (ConnectionState.Closed, ConnectionState.Open);
  357. } catch {
  358. // free handles if any.
  359. FreeHandles ();
  360. throw;
  361. }
  362. disposed = false;
  363. }
  364. [MonoTODO]
  365. public static void ReleaseObjectPool ()
  366. {
  367. throw new NotImplementedException ();
  368. }
  369. private void FreeHandles ()
  370. {
  371. OdbcReturn ret = OdbcReturn.Error;
  372. if (hdbc != IntPtr.Zero) {
  373. ret = libodbc.SQLFreeHandle ((ushort) OdbcHandleType.Dbc, hdbc);
  374. if ( (ret != OdbcReturn.Success) && (ret != OdbcReturn.SuccessWithInfo))
  375. throw CreateOdbcException (OdbcHandleType.Dbc, hdbc);
  376. }
  377. hdbc = IntPtr.Zero;
  378. if (henv != IntPtr.Zero) {
  379. ret = libodbc.SQLFreeHandle ((ushort) OdbcHandleType.Env, henv);
  380. if ( (ret != OdbcReturn.Success) && (ret != OdbcReturn.SuccessWithInfo))
  381. throw CreateOdbcException (OdbcHandleType.Env, henv);
  382. }
  383. henv = IntPtr.Zero;
  384. }
  385. #if NET_2_0
  386. public override DataTable GetSchema ()
  387. {
  388. if (State == ConnectionState.Closed)
  389. throw ExceptionHelper.ConnectionClosed ();
  390. return MetaDataCollections.Instance;
  391. }
  392. public override DataTable GetSchema (string collectionName)
  393. {
  394. return GetSchema (collectionName, null);
  395. }
  396. public override DataTable GetSchema (string collectionName, string [] restrictionValues)
  397. {
  398. if (State == ConnectionState.Closed)
  399. throw ExceptionHelper.ConnectionClosed ();
  400. return GetSchema (collectionName, null);
  401. }
  402. [MonoTODO]
  403. public override void EnlistTransaction (Transaction transaction)
  404. {
  405. throw new NotImplementedException ();
  406. }
  407. #endif
  408. [MonoTODO]
  409. public void EnlistDistributedTransaction (ITransaction transaction)
  410. {
  411. throw new NotImplementedException ();
  412. }
  413. internal string GetInfo (OdbcInfo info)
  414. {
  415. if (State == ConnectionState.Closed)
  416. throw new InvalidOperationException ("The connection is closed.");
  417. OdbcReturn ret = OdbcReturn.Error;
  418. short max_length = 512;
  419. byte [] buffer = new byte [512];
  420. short actualLength = 0;
  421. ret = libodbc.SQLGetInfo (hdbc, info, buffer, max_length, ref actualLength);
  422. if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
  423. throw CreateOdbcException (OdbcHandleType.Dbc, hdbc);
  424. return Encoding.Unicode.GetString (buffer, 0, actualLength);
  425. }
  426. string GetSafeInfo (OdbcInfo info)
  427. {
  428. if (State == ConnectionState.Closed)
  429. return null;
  430. OdbcReturn ret = OdbcReturn.Error;
  431. short max_length = 512;
  432. byte [] buffer = new byte [512];
  433. short actualLength = 0;
  434. ret = libodbc.SQLGetInfo (hdbc, info, buffer, max_length, ref actualLength);
  435. if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
  436. return null;
  437. return Encoding.Unicode.GetString (buffer, 0, actualLength);
  438. }
  439. private void RaiseStateChange (ConnectionState from, ConnectionState to)
  440. {
  441. #if ONLY_1_1
  442. if (StateChange != null)
  443. StateChange (this, new StateChangeEventArgs (from, to));
  444. #else
  445. base.OnStateChange (new StateChangeEventArgs (from, to));
  446. #endif
  447. }
  448. private OdbcInfoMessageEventArgs CreateOdbcInfoMessageEvent (OdbcErrorCollection errors)
  449. {
  450. return new OdbcInfoMessageEventArgs (errors);
  451. }
  452. private void OnOdbcInfoMessage (OdbcInfoMessageEventArgs e)
  453. {
  454. if (InfoMessage != null)
  455. InfoMessage (this, e);
  456. }
  457. internal OdbcException CreateOdbcException (OdbcHandleType HandleType, IntPtr Handle)
  458. {
  459. short buflen = 256;
  460. short txtlen = 0;
  461. int nativeerror = 0;
  462. OdbcReturn ret = OdbcReturn.Success;
  463. ushort recordNumber = 1;
  464. OdbcErrorCollection errors = new OdbcErrorCollection ();
  465. while (true) {
  466. byte [] buf_MsgText = new byte [buflen * 2];
  467. byte [] buf_SqlState = new byte [buflen * 2];
  468. switch (HandleType) {
  469. case OdbcHandleType.Dbc:
  470. ret = libodbc.SQLError (IntPtr.Zero, Handle, IntPtr.Zero, buf_SqlState,
  471. ref nativeerror, buf_MsgText, buflen, ref txtlen);
  472. break;
  473. case OdbcHandleType.Stmt:
  474. ret = libodbc.SQLError (IntPtr.Zero, IntPtr.Zero, Handle, buf_SqlState,
  475. ref nativeerror, buf_MsgText, buflen, ref txtlen);
  476. break;
  477. case OdbcHandleType.Env:
  478. ret = libodbc.SQLError (Handle, IntPtr.Zero, IntPtr.Zero, buf_SqlState,
  479. ref nativeerror, buf_MsgText, buflen, ref txtlen);
  480. break;
  481. }
  482. if (ret != OdbcReturn.Success)
  483. break;
  484. string state = RemoveTrailingNullChar (Encoding.Unicode.GetString (buf_SqlState));
  485. string message = Encoding.Unicode.GetString (buf_MsgText, 0, txtlen * 2);
  486. errors.Add (new OdbcError (message, state, nativeerror));
  487. }
  488. string source = SafeDriver;
  489. foreach (OdbcError error in errors)
  490. error.SetSource (source);
  491. return new OdbcException (errors);
  492. }
  493. static string RemoveTrailingNullChar (string value)
  494. {
  495. return value.TrimEnd ('\0');
  496. }
  497. #endregion
  498. #region Events and Delegates
  499. #if ONLY_1_1
  500. [OdbcDescription ("DbConnection_StateChange")]
  501. [OdbcCategory ("DataCategory_StateChange")]
  502. public event StateChangeEventHandler StateChange;
  503. #endif // ONLY_1_1
  504. [OdbcDescription ("DbConnection_InfoMessage")]
  505. [OdbcCategory ("DataCategory_InfoMessage")]
  506. public event OdbcInfoMessageEventHandler InfoMessage;
  507. private void MessageHandler (OdbcException e)
  508. {
  509. OnOdbcInfoMessage (CreateOdbcInfoMessageEvent (e.Errors));
  510. }
  511. #endregion
  512. }
  513. }