OdbcConnection.cs 16 KB

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