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