OracleConnection.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611
  1. //
  2. // OracleConnection.cs
  3. //
  4. // Part of the Mono class libraries at
  5. // mcs/class/System.Data.OracleClient/System.Data.OracleClient
  6. //
  7. // Assembly: System.Data.OracleClient.dll
  8. // Namespace: System.Data.OracleClient
  9. //
  10. // Authors:
  11. // Daniel Morgan <[email protected]>
  12. // Tim Coleman <[email protected]>
  13. // Hubert FONGARNAND <[email protected]>
  14. //
  15. // Copyright (C) Daniel Morgan, 2002, 2005, 2006
  16. // Copyright (C) Tim Coleman, 2003
  17. // Copyright (C) Hubert FONGARNAND, 2005
  18. //
  19. // Original source code for setting ConnectionString
  20. // by Tim Coleman <[email protected]>
  21. //
  22. // Copyright (C) Tim Coleman, 2002
  23. //
  24. // Licensed under the MIT/X11 License.
  25. //
  26. using System;
  27. using System.Collections;
  28. using System.Collections.Specialized;
  29. using System.ComponentModel;
  30. using System.Data;
  31. using System.Data.OracleClient.Oci;
  32. using System.Drawing.Design;
  33. using System.EnterpriseServices;
  34. using System.Globalization;
  35. using System.Text;
  36. namespace System.Data.OracleClient
  37. {
  38. internal struct OracleConnectionInfo
  39. {
  40. internal string Username;
  41. internal string Password;
  42. internal string Database;
  43. internal string ConnectionString;
  44. internal OciCredentialType CredentialType;
  45. }
  46. [DefaultEvent ("InfoMessage")]
  47. public sealed class OracleConnection : Component, ICloneable, IDbConnection
  48. {
  49. #region Fields
  50. OciGlue oci;
  51. ConnectionState state;
  52. OracleConnectionInfo conInfo;
  53. OracleTransaction transaction = null;
  54. string connectionString = String.Empty;
  55. string parsedConnectionString = String.Empty;
  56. OracleDataReader dataReader = null;
  57. bool pooling = true;
  58. static OracleConnectionPoolManager pools = new OracleConnectionPoolManager ();
  59. OracleConnectionPool pool;
  60. int minPoolSize = 0;
  61. int maxPoolSize = 100;
  62. byte persistSecurityInfo = 1;
  63. bool disposed = false;
  64. #endregion // Fields
  65. #region Constructors
  66. public OracleConnection ()
  67. {
  68. state = ConnectionState.Closed;
  69. }
  70. public OracleConnection (string connectionString)
  71. : this()
  72. {
  73. SetConnectionString (connectionString, false);
  74. }
  75. #endregion // Constructors
  76. #region Properties
  77. int IDbConnection.ConnectionTimeout {
  78. [MonoTODO]
  79. get { return -1; }
  80. }
  81. string IDbConnection.Database {
  82. [MonoTODO]
  83. get { return String.Empty; }
  84. }
  85. internal OracleDataReader DataReader {
  86. get { return dataReader; }
  87. set { dataReader = value; }
  88. }
  89. internal OciEnvironmentHandle Environment {
  90. get { return oci.Environment; }
  91. }
  92. internal OciErrorHandle ErrorHandle {
  93. get { return oci.ErrorHandle; }
  94. }
  95. internal OciServiceHandle ServiceContext {
  96. get { return oci.ServiceContext; }
  97. }
  98. internal OciSessionHandle Session {
  99. get { return oci.SessionHandle; }
  100. }
  101. [MonoTODO]
  102. [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
  103. public string DataSource {
  104. get {
  105. return conInfo.Database;
  106. }
  107. }
  108. [Browsable (false)]
  109. [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
  110. public ConnectionState State {
  111. get { return state; }
  112. }
  113. [DefaultValue ("")]
  114. [RecommendedAsConfigurable (true)]
  115. [RefreshProperties (RefreshProperties.All)]
  116. [Editor ("Microsoft.VSDesigner.Data.Oracle.Design.OracleConnectionStringEditor, " + Consts.AssemblyMicrosoft_VSDesigner, typeof(UITypeEditor))]
  117. public string ConnectionString {
  118. get {
  119. return parsedConnectionString;
  120. }
  121. set {
  122. SetConnectionString (value, false);
  123. }
  124. }
  125. [MonoTODO]
  126. [Browsable (false)]
  127. [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
  128. public string ServerVersion {
  129. get {
  130. if (this.State != ConnectionState.Open)
  131. throw new System.InvalidOperationException ("Invalid operation. The connection is closed.");
  132. return GetOracleVersion ();
  133. }
  134. }
  135. internal string GetOracleVersion ()
  136. {
  137. byte[] buffer = new Byte[256];
  138. uint bufflen = (uint) buffer.Length;
  139. IntPtr sh = oci.ServiceContext;
  140. IntPtr eh = oci.ErrorHandle;
  141. OciCalls.OCIServerVersion (sh, eh, ref buffer, bufflen, OciHandleType.Service);
  142. // Get length of returned string
  143. int rsize = 0;
  144. IntPtr env = oci.Environment;
  145. OciCalls.OCICharSetToUnicode (env, null, buffer, out rsize);
  146. // Get string
  147. StringBuilder ret = new StringBuilder(rsize);
  148. OciCalls.OCICharSetToUnicode (env, ret, buffer, out rsize);
  149. return ret.ToString ();
  150. }
  151. internal OciGlue Oci {
  152. get { return oci; }
  153. }
  154. internal OracleTransaction Transaction {
  155. get { return transaction; }
  156. set { transaction = value; }
  157. }
  158. #endregion // Properties
  159. #region Methods
  160. public OracleTransaction BeginTransaction ()
  161. {
  162. return BeginTransaction (IsolationLevel.ReadCommitted);
  163. }
  164. public OracleTransaction BeginTransaction (IsolationLevel il)
  165. {
  166. if (state == ConnectionState.Closed)
  167. throw new InvalidOperationException ("The connection is not open.");
  168. if (transaction != null)
  169. throw new InvalidOperationException ("OracleConnection does not support parallel transactions.");
  170. OciTransactionHandle transactionHandle = oci.CreateTransaction ();
  171. if (transactionHandle == null)
  172. throw new Exception("Error: Unable to start transaction");
  173. else {
  174. transactionHandle.Begin ();
  175. transaction = new OracleTransaction (this, il, transactionHandle);
  176. }
  177. return transaction;
  178. }
  179. [MonoTODO]
  180. void IDbConnection.ChangeDatabase (string databaseName)
  181. {
  182. throw new NotImplementedException ();
  183. }
  184. public OracleCommand CreateCommand ()
  185. {
  186. OracleCommand command = new OracleCommand ();
  187. command.Connection = this;
  188. return command;
  189. }
  190. [MonoTODO]
  191. object ICloneable.Clone ()
  192. {
  193. OracleConnection con = new OracleConnection ();
  194. con.SetConnectionString (connectionString, true);
  195. // TODO: what other properties need to be cloned?
  196. return con;
  197. }
  198. IDbTransaction IDbConnection.BeginTransaction ()
  199. {
  200. return BeginTransaction ();
  201. }
  202. IDbTransaction IDbConnection.BeginTransaction (IsolationLevel iso)
  203. {
  204. return BeginTransaction (iso);
  205. }
  206. IDbCommand IDbConnection.CreateCommand ()
  207. {
  208. return CreateCommand ();
  209. }
  210. void IDisposable.Dispose ()
  211. {
  212. Dispose (true);
  213. GC.SuppressFinalize (this);
  214. }
  215. [MonoTODO]
  216. protected override void Dispose (bool disposing)
  217. {
  218. if (!disposed) {
  219. if (State == ConnectionState.Open)
  220. Close ();
  221. dataReader = null;
  222. transaction = null;
  223. oci = null;
  224. pool = null;
  225. conInfo.Username = "";
  226. conInfo.Database = "";
  227. conInfo.Password = "";
  228. connectionString = "";
  229. parsedConnectionString = "";
  230. base.Dispose (disposing);
  231. disposed = true;
  232. }
  233. }
  234. [MonoTODO]
  235. public void EnlistDistributedTransaction (ITransaction distributedTransaction)
  236. {
  237. throw new NotImplementedException ();
  238. }
  239. // Get NLS_DATE_FORMAT string from Oracle server
  240. internal string GetSessionDateFormat ()
  241. {
  242. // 23 is 22 plus 1 for NUL terminated character
  243. // a DATE format has a max size of 22
  244. return GetNlsInfo (Session, 23, OciNlsServiceType.DATEFORMAT);
  245. }
  246. // Get NLS Info
  247. //
  248. // handle = OciEnvironmentHandle or OciSessionHandle
  249. // bufflen = Length of byte buffer to allocate to retrieve the NLS info
  250. // item = OciNlsServiceType enum value
  251. //
  252. // if unsure how much you need, use OciNlsServiceType.MAXBUFSZ
  253. internal string GetNlsInfo (OciHandle handle, uint bufflen, OciNlsServiceType item)
  254. {
  255. byte[] buffer = new Byte[bufflen];
  256. OciCalls.OCINlsGetInfo (handle, ErrorHandle,
  257. ref buffer, bufflen, (ushort) item);
  258. // Get length of returned string
  259. int rsize = 0;
  260. OciCalls.OCICharSetToUnicode (Environment, null, buffer, out rsize);
  261. // Get string
  262. StringBuilder ret = new StringBuilder (rsize);
  263. OciCalls.OCICharSetToUnicode (Environment, ret, buffer, out rsize);
  264. return ret.ToString ();
  265. }
  266. public void Open ()
  267. {
  268. PersistSecurityInfo ();
  269. if (!pooling) {
  270. oci = new OciGlue ();
  271. oci.CreateConnection (conInfo);
  272. }
  273. else {
  274. pool = pools.GetConnectionPool (conInfo, minPoolSize, maxPoolSize);
  275. oci = pool.GetConnection ();
  276. }
  277. state = ConnectionState.Open;
  278. CreateStateChange (ConnectionState.Closed, ConnectionState.Open);
  279. }
  280. internal void CreateInfoMessage (OciErrorInfo info)
  281. {
  282. OracleInfoMessageEventArgs a = new OracleInfoMessageEventArgs (info);
  283. OnInfoMessage (a);
  284. }
  285. private void OnInfoMessage (OracleInfoMessageEventArgs e)
  286. {
  287. if (InfoMessage != null)
  288. InfoMessage (this, e);
  289. }
  290. internal void CreateStateChange (ConnectionState original, ConnectionState current)
  291. {
  292. StateChangeEventArgs a = new StateChangeEventArgs (original, current);
  293. OnStateChange (a);
  294. }
  295. private void OnStateChange (StateChangeEventArgs e)
  296. {
  297. if (StateChange != null)
  298. StateChange (this, e);
  299. }
  300. public void Close ()
  301. {
  302. if (transaction != null)
  303. transaction.Rollback ();
  304. if (!pooling)
  305. oci.Disconnect ();
  306. else if (pool != null)
  307. pool.ReleaseConnection (oci);
  308. state = ConnectionState.Closed;
  309. CreateStateChange (ConnectionState.Open, ConnectionState.Closed);
  310. }
  311. private void PersistSecurityInfo ()
  312. {
  313. // persistSecurityInfo:
  314. // 0 = true/yes
  315. // 1 = false/no (have not parsed out password yet)
  316. // 2 = like 1, but have parsed out password
  317. if (persistSecurityInfo == 0 || persistSecurityInfo == 2)
  318. return;
  319. persistSecurityInfo = 2;
  320. if (connectionString == null)
  321. return;
  322. if (connectionString == String.Empty)
  323. return;
  324. string conString = connectionString + ";";
  325. bool inQuote = false;
  326. bool inDQuote = false;
  327. string name = String.Empty;
  328. StringBuilder sb = new StringBuilder ();
  329. int nStart = 0;
  330. int nFinish = 0;
  331. int i = -1;
  332. foreach (char c in conString) {
  333. i ++;
  334. switch (c) {
  335. case '\'':
  336. inQuote = !inQuote;
  337. break;
  338. case '"' :
  339. inDQuote = !inDQuote;
  340. break;
  341. case ';' :
  342. if (!inDQuote && !inQuote) {
  343. if (name != String.Empty && name != null) {
  344. name = name.ToUpper ().Trim ();
  345. if (name.Equals ("PASSWORD") || name.Equals ("PWD")) {
  346. nFinish = i;
  347. string part1 = String.Empty;
  348. string part3 = String.Empty;
  349. sb = new StringBuilder ();
  350. if (nStart > 0) {
  351. part1 = conString.Substring (0, nStart);
  352. if (part1[part1.Length - 1] == ';')
  353. part1 = part1.Substring (0, part1.Length - 1);
  354. sb.Append (part1);
  355. }
  356. if (!part1.Equals (String.Empty))
  357. sb.Append (';');
  358. if (conString.Length - nFinish - 1 > 0) {
  359. part3 = conString.Substring (nFinish, conString.Length - nFinish);
  360. if (part3[0] == ';')
  361. part3 = part3.Substring(1, part3.Length - 1);
  362. sb.Append (part3);
  363. }
  364. parsedConnectionString = sb.ToString ();
  365. return;
  366. }
  367. }
  368. name = String.Empty;
  369. sb = new StringBuilder ();
  370. nStart = i;
  371. nFinish = i;
  372. }
  373. else
  374. sb.Append (c);
  375. break;
  376. case '=' :
  377. if (!inDQuote && !inQuote) {
  378. name = sb.ToString ();
  379. sb = new StringBuilder ();
  380. }
  381. else
  382. sb.Append (c);
  383. break;
  384. default:
  385. sb.Append (c);
  386. break;
  387. }
  388. }
  389. }
  390. internal void SetConnectionString (string connectionString, bool persistSecurity)
  391. {
  392. persistSecurityInfo = 1;
  393. this.connectionString = String.Copy (connectionString);
  394. this.parsedConnectionString = this.connectionString;
  395. if (this.connectionString == null)
  396. this.connectionString = String.Empty;
  397. conInfo.Username = "";
  398. conInfo.Database = "";
  399. conInfo.Password = "";
  400. conInfo.CredentialType = OciCredentialType.RDBMS;
  401. if (connectionString == null)
  402. return;
  403. if (connectionString == String.Empty)
  404. return;
  405. connectionString += ";";
  406. NameValueCollection parameters = new NameValueCollection ();
  407. bool inQuote = false;
  408. bool inDQuote = false;
  409. string name = String.Empty;
  410. string value = String.Empty;
  411. StringBuilder sb = new StringBuilder ();
  412. foreach (char c in connectionString) {
  413. switch (c) {
  414. case '\'':
  415. inQuote = !inQuote;
  416. break;
  417. case '"' :
  418. inDQuote = !inDQuote;
  419. break;
  420. case ';' :
  421. if (!inDQuote && !inQuote) {
  422. if (name != String.Empty && name != null) {
  423. name = name.ToUpper ().Trim ();
  424. value = sb.ToString ().Trim ();
  425. parameters [name] = value;
  426. }
  427. name = String.Empty;
  428. value = String.Empty;
  429. sb = new StringBuilder ();
  430. }
  431. else
  432. sb.Append (c);
  433. break;
  434. case '=' :
  435. if (!inDQuote && !inQuote) {
  436. name = sb.ToString ();
  437. sb = new StringBuilder ();
  438. }
  439. else
  440. sb.Append (c);
  441. break;
  442. default:
  443. sb.Append (c);
  444. break;
  445. }
  446. }
  447. SetProperties (parameters);
  448. conInfo.ConnectionString = this.connectionString;
  449. if (persistSecurity == true)
  450. PersistSecurityInfo ();
  451. }
  452. private void SetProperties (NameValueCollection parameters)
  453. {
  454. string value;
  455. foreach (string name in parameters) {
  456. value = parameters[name];
  457. switch (name) {
  458. case "UNICODE":
  459. break;
  460. case "ENLIST":
  461. break;
  462. case "CONNECTION LIFETIME":
  463. // TODO:
  464. break;
  465. case "INTEGRATED SECURITY":
  466. if (ConvertToBoolean ("integrated security", value) == false)
  467. conInfo.CredentialType = OciCredentialType.RDBMS;
  468. else
  469. conInfo.CredentialType = OciCredentialType.External;
  470. break;
  471. case "PERSIST SECURITY INFO":
  472. if (ConvertToBoolean ("persist security info", value) == false)
  473. persistSecurityInfo = 1;
  474. else
  475. persistSecurityInfo = 0;
  476. break;
  477. case "MIN POOL SIZE":
  478. minPoolSize = int.Parse (value);
  479. break;
  480. case "MAX POOL SIZE":
  481. maxPoolSize = int.Parse (value);
  482. break;
  483. case "DATA SOURCE" :
  484. case "SERVER" :
  485. conInfo.Database = value;
  486. break;
  487. case "PASSWORD" :
  488. case "PWD" :
  489. conInfo.Password = value;
  490. break;
  491. case "UID" :
  492. case "USER ID" :
  493. conInfo.Username = value;
  494. break;
  495. case "POOLING" :
  496. pooling = ConvertToBoolean("pooling", value);
  497. break;
  498. default:
  499. throw new ArgumentException("Connection parameter not supported: '" + name + "'");
  500. }
  501. }
  502. }
  503. private bool ConvertToBoolean(string key, string value)
  504. {
  505. string upperValue = value.ToUpper();
  506. if (upperValue == "TRUE" ||upperValue == "YES") {
  507. return true;
  508. }
  509. else if (upperValue == "FALSE" || upperValue == "NO") {
  510. return false;
  511. }
  512. throw new ArgumentException(string.Format(CultureInfo.InvariantCulture,
  513. "Invalid value \"{0}\" for key '{1}'.", value, key));
  514. }
  515. ~OracleConnection()
  516. {
  517. Dispose (false);
  518. }
  519. #endregion // Methods
  520. public event OracleInfoMessageEventHandler InfoMessage;
  521. public event StateChangeEventHandler StateChange;
  522. }
  523. }