New test.
[mono.git] / mcs / class / FirebirdSql.Data.Firebird / FirebirdSql.Data.Firebird / FbConnection.cs
1 /*
2  *      Firebird ADO.NET Data provider for .NET and Mono 
3  * 
4  *         The contents of this file are subject to the Initial 
5  *         Developer's Public License Version 1.0 (the "License"); 
6  *         you may not use this file except in compliance with the 
7  *         License. You may obtain a copy of the License at 
8  *         http://www.firebirdsql.org/index.php?op=doc&id=idpl
9  *
10  *         Software distributed under the License is distributed on 
11  *         an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either 
12  *         express or implied. See the License for the specific 
13  *         language governing rights and limitations under the License.
14  * 
15  *      Copyright (c) 2002, 2005 Carlos Guzman Alvarez
16  *      All Rights Reserved.
17  */
18
19 using System;
20 using System.Collections;
21 using System.ComponentModel;
22 using System.Data;
23 using System.Drawing;
24 using System.Globalization;
25 using System.Text;
26
27 using FirebirdSql.Data.Common;
28
29 namespace FirebirdSql.Data.Firebird
30 {
31         /// <include file='Doc/en_EN/FbConnection.xml' path='doc/class[@name="FbConnection"]/overview/*'/>
32 #if     (!NETCF)
33         [ToolboxItem(true)]
34         [ToolboxBitmap(typeof(FbConnection), "Resources.FbConnection.bmp")]
35         [DefaultEvent("InfoMessage")]
36 #endif
37         public sealed class FbConnection : Component, IDbConnection, ICloneable
38         {
39                 #region Events
40
41                 /// <include file='Doc/en_EN/FbConnection.xml' path='doc/class[@name="FbConnection"]/event[@name="StateChange"]/*'/>
42                 public event StateChangeEventHandler StateChange;
43
44                 /// <include file='Doc/en_EN/FbConnection.xml' path='doc/class[@name="FbConnection"]/event[@name="InfoMessage"]/*'/>
45                 public event FbInfoMessageEventHandler InfoMessage;
46
47                 #endregion
48
49                 #region Fields
50
51                 private FbConnectionInternal innerConnection;
52                 private ConnectionState         state;
53                 private FbConnectionString      options;
54                 private bool                            disposed;
55                 private string                          connectionString;
56
57                 #endregion
58
59                 #region Properties
60
61                 /// <include file='Doc/en_EN/FbConnection.xml' path='doc/class[@name="FbConnection"]/property[@name="ConnectionString"]/*'/>
62 #if     (NET)
63                 [Category("Data"), RecommendedAsConfigurableAttribute(true), RefreshProperties(RefreshProperties.All), DefaultValue("")]
64                 [Editor(typeof(Design.FbConnectionStringUIEditor), typeof(System.Drawing.Design.UITypeEditor))]
65 #endif
66                 public string ConnectionString
67                 {
68                         get { return this.connectionString; }
69                         set
70                         {
71                                 lock (this)
72                                 {
73                                         if (this.state == ConnectionState.Closed)
74                                         {
75                                                 if (value == null)
76                                                 {
77                                                         value = "";
78                                                 }
79
80                                                 this.options.Load(value);
81                                                 this.options.Validate();
82                                                 this.connectionString = value;
83                                         }
84                                 }
85                         }
86                 }
87
88                 /// <include file='Doc/en_EN/FbConnection.xml' path='doc/class[@name="FbConnection"]/property[@name="ConnectionTimeout"]/*'/>
89 #if     (!NETCF)
90                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
91 #endif
92                 public int ConnectionTimeout
93                 {
94                         get { return this.options.ConnectionTimeout; }
95                 }
96
97                 /// <include file='Doc/en_EN/FbConnection.xml' path='doc/class[@name="FbConnection"]/property[@name="Database"]/*'/>
98 #if     (!NETCF)
99                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
100 #endif
101                 public string Database
102                 {
103                         get { return this.options.Database; }
104                 }
105
106                 /// <include file='Doc/en_EN/FbConnection.xml' path='doc/class[@name="FbConnection"]/property[@name="DataSource"]/*'/>
107 #if     (!NETCF)
108                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
109 #endif
110                 public string DataSource
111                 {
112                         get { return this.options.DataSource; }
113                 }
114
115                 /// <include file='Doc/en_EN/FbConnection.xml' path='doc/class[@name="FbConnection"]/property[@name="ServerVersion"]/*'/>
116 #if     (!NETCF)
117                 [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
118 #endif
119                 public string ServerVersion
120                 {
121                         get
122                         {
123                                 if (this.state == ConnectionState.Closed)
124                                 {
125                                         throw new InvalidOperationException("The connection is closed.");
126                                 }
127
128                                 if (this.innerConnection != null)
129                                 {
130                                         return this.innerConnection.Database.ServerVersion;
131                                 }
132
133                                 return String.Empty;
134                         }
135                 }
136
137                 /// <include file='Doc/en_EN/FbConnection.xml' path='doc/class[@name="FbConnection"]/property[@name="State"]/*'/>
138 #if     (!NETCF)
139                 [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
140 #endif
141                 public ConnectionState State
142                 {
143                         get { return this.state; }
144                 }
145
146                 /// <include file='Doc/en_EN/FbConnection.xml' path='doc/class[@name="FbConnection"]/property[@name="PacketSize"]/*'/>
147 #if     (!NETCF)
148                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
149 #endif
150                 public int PacketSize
151                 {
152                         get { return this.options.PacketSize; }
153                 }
154
155                 #endregion
156
157                 #region Internal Properties
158
159                 internal FbConnectionInternal InnerConnection
160                 {
161                         get { return this.innerConnection; }
162                 }
163
164                 internal FbConnectionString ConnectionOptions
165                 {
166                         get { return this.options; }
167                 }
168
169                 internal bool IsClosed
170                 {
171                         get { return this.state == ConnectionState.Closed; }
172                 }
173
174                 #endregion
175
176                 #region Constructors
177
178                 /// <include file='Doc/en_EN/FbConnection.xml' path='doc/class[@name="FbConnection"]/constructor[@name="ctor"]/*'/>
179                 public FbConnection() : this(null)
180                 {
181                 }
182
183                 /// <include file='Doc/en_EN/FbConnection.xml' path='doc/class[@name="FbConnection"]/constructor[@name="ctor(System.String)"]/*'/>      
184                 public FbConnection(string connectionString) : base()
185                 {
186                         this.options = new FbConnectionString();
187                         this.state = ConnectionState.Closed;
188                         this.connectionString = "";
189
190                         if (connectionString != null)
191                         {
192                                 this.ConnectionString = connectionString;
193                         }
194                 }
195
196                 #endregion
197
198                 #region IDisposable     Methods
199
200                 /// <include file='Doc/en_EN/FbConnection.xml' path='doc/class[@name="FbConnection"]/method[@name="Dispose(System.Boolean)"]/*'/>
201                 protected override void Dispose(bool disposing)
202                 {
203                         lock (this)
204                         {
205                                 if (!this.disposed)
206                                 {
207                                         try
208                                         {
209                                                 // release any unmanaged resources
210                                                 this.Close();
211
212                                                 if (disposing)
213                                                 {
214                                                         // release any managed resources
215                                                         this.innerConnection = null;
216                                                 }
217
218                                                 this.disposed = true;
219                                         }
220                                         catch
221                                         {
222                                         }
223                                         finally
224                                         {
225                                                 base.Dispose(disposing);
226                                         }
227                                 }
228                         }
229                 }
230
231                 #endregion
232
233                 #region ICloneable Methods
234
235                 object ICloneable.Clone()
236                 {
237                         return new FbConnection(this.ConnectionString);
238                 }
239
240                 #endregion
241
242                 #region Static Properties
243
244                 /// <include file='Doc/en_EN/FbConnection.xml' path='doc/class[@name="FbConnection"]/property[@name="ConnectionPoolsCount"]/*'/>
245                 public static int ConnectionPoolsCount
246                 {
247                         get { return FbPoolManager.Instance.PoolsCount; }
248                 }
249
250                 #endregion
251
252                 #region Static Methods
253
254                 /// <include file='Doc/en_EN/FbConnection.xml' path='doc/class[@name="FbConnection"]/method[@name="GetPooledConnectionCount(FbConnection)"]/*'/>
255                 public static int GetPooledConnectionCount(FbConnection connection)
256                 {
257                         FbPoolManager manager = FbPoolManager.Instance;
258                         FbConnectionPool pool = manager.FindPool(connection.ConnectionString);
259
260                         if (pool != null)
261                         {
262                                 return pool.Count;
263                         }
264
265                         return 0;
266                 }
267
268                 /// <include file='Doc/en_EN/FbConnection.xml' path='doc/class[@name="FbConnection"]/method[@name="ClearAllPools"]/*'/>
269                 public static void ClearAllPools()
270                 {
271                         FbPoolManager manager = FbPoolManager.Instance;
272
273                         manager.ClearAllPools();
274                 }
275
276                 /// <include file='Doc/en_EN/FbConnection.xml' path='doc/class[@name="FbConnection"]/method[@name="ClearPool(FbConnection)"]/*'/>
277                 public static void ClearPool(FbConnection connection)
278                 {
279                         FbPoolManager manager = FbPoolManager.Instance;
280
281                         manager.ClearPool(connection.ConnectionString);
282                 }
283
284                 /// <include file='Doc/en_EN/FbConnection.xml' path='doc/class[@name="FbConnection"]/method[@name="CreateDatabase(System.String)"]/*'/>
285                 public static void CreateDatabase(string connectionString)
286                 {
287                         FbConnection.CreateDatabase(connectionString, 4096, true, false);
288                 }
289
290                 /// <include file='Doc/en_EN/FbConnection.xml' path='doc/class[@name="FbConnection"]/method[@name="CreateDatabase(System.String, System.Boolean)"]/*'/>
291                 public static void CreateDatabase(string connectionString, bool overwrite)
292                 {
293                         FbConnection.CreateDatabase(connectionString, 4096, true, overwrite);
294                 }
295
296                 /// <include file='Doc/en_EN/FbConnection.xml' path='doc/class[@name="FbConnection"]/method[@name="CreateDatabase(System.String,System.Int32,System.Boolean,System.Boolean)"]/*'/>
297                 public static void CreateDatabase(
298                         string connectionString, int pageSize, bool forcedWrites, bool overwrite)
299                 {
300                         FbConnectionString options = new FbConnectionString(connectionString);
301                         options.Validate();
302
303                         try
304                         {
305                                 // DPB configuration
306                                 DatabaseParameterBuffer dpb = new DatabaseParameterBuffer();
307
308                                 // Dpb version
309                                 dpb.Append(IscCodes.isc_dpb_version1);
310
311                                 // Dummy packet interval
312                                 dpb.Append(IscCodes.isc_dpb_dummy_packet_interval, new byte[] { 120, 10, 0, 0 });
313
314                                 // User name
315                                 dpb.Append(IscCodes.isc_dpb_user_name, options.UserID);
316
317                                 // User password
318                                 dpb.Append(IscCodes.isc_dpb_password, options.Password);
319
320                                 // Database     dialect
321                                 dpb.Append(IscCodes.isc_dpb_sql_dialect, new byte[] { options.Dialect, 0, 0, 0 });
322
323                                 // Database overwrite
324                                 dpb.Append(IscCodes.isc_dpb_overwrite, (short)(overwrite ? 1 : 0));
325
326                                 // Character set
327                                 if (options.Charset.Length > 0)
328                                 {
329                                         int index = Charset.SupportedCharsets.IndexOf(options.Charset);
330
331                                         if (index == -1)
332                                         {
333                                                 throw new ArgumentException("Character set is not valid.");
334                                         }
335                                         else
336                                         {
337                                                 dpb.Append(
338                                                         IscCodes.isc_dpb_set_db_charset,
339                                                         Charset.SupportedCharsets[index].Name);
340                                         }
341                                 }
342
343                                 // Page Size
344                                 if (pageSize > 0)
345                                 {
346                                         dpb.Append(IscCodes.isc_dpb_page_size, pageSize);
347                                 }
348
349                                 // Forced writes
350                                 dpb.Append(IscCodes.isc_dpb_force_write, (short)(forcedWrites ? 1 : 0));
351
352                                 if (!overwrite)
353                                 {
354                                         // Check if     the     database exists
355                                         FbConnectionInternal c = new FbConnectionInternal(options);
356
357                                         try
358                                         {
359                                                 c.Connect();
360                                                 c.Disconnect();
361
362                                                 IscException ex = new IscException(IscCodes.isc_db_or_file_exists);
363                                                 throw new FbException(ex.Message, ex);
364                                         }
365                                         catch (FbException ex)
366                                         {
367                                                 if (ex.ErrorCode != 335544344)
368                                                 {
369                                                         throw;
370                                                 }
371                                         }
372                                 }
373
374                                 // Create the new database
375                                 FbConnectionInternal db = new FbConnectionInternal(options);
376                                 db.CreateDatabase(dpb);
377                         }
378                         catch (IscException ex)
379                         {
380                                 throw new FbException(ex.Message, ex);
381                         }
382                 }
383
384                 /// <include file='Doc/en_EN/FbConnection.xml' path='doc/class[@name="FbConnection"]/method[@name="DropDatabase(System.String)"]/*'/>
385                 public static void DropDatabase(string connectionString)
386                 {
387                         // Configure Attachment
388                         FbConnectionString options = new FbConnectionString(connectionString);
389                         options.Validate();
390
391                         try
392                         {
393                                 // Drop the     database        
394                                 FbConnectionInternal db = new FbConnectionInternal(options);
395                                 db.DropDatabase();
396                         }
397                         catch (IscException ex)
398                         {
399                                 throw new FbException(ex.Message, ex);
400                         }
401                 }
402
403                 /// <include file='Doc/en_EN/FbConnection.xml' path='doc/class[@name="FbConnection"]/method[@name="CreateDatabase(System.Collections.Hashtable)"]/*'/>
404                 [Obsolete("Use CreateDatabase(string connectionString) instead")]
405                 public static void CreateDatabase(Hashtable values)
406                 {
407                         bool overwrite = false;
408                         int index = 0;
409                         byte dialect = 3;
410                         int serverType = 0;
411
412                         if (!values.ContainsKey("User") ||
413                                 !values.ContainsKey("Password") ||
414                                 !values.ContainsKey("Database"))
415                         {
416                                 throw new ArgumentException("CreateDatabase requires a user name, password and database path.");
417                         }
418
419                         if (values.ContainsKey("ServerType"))
420                         {
421                                 serverType = Convert.ToInt32(values["ServerType"], CultureInfo.InvariantCulture);
422                         }
423
424                         if (!values.ContainsKey("DataSource"))
425                         {
426                                 values.Add("DataSource", "localhost");
427                         }
428
429                         if (!values.ContainsKey("Port"))
430                         {
431                                 values.Add("Port", 3050);
432                         }
433
434                         if (values.ContainsKey("Dialect"))
435                         {
436                                 dialect = Convert.ToByte(values["Dialect"], CultureInfo.InvariantCulture);
437                         }
438
439                         if (dialect < 1 || dialect > 3)
440                         {
441                                 throw new ArgumentException("Incorrect database dialect it should be 1, 2, or 3.");
442                         }
443
444                         if (values.ContainsKey("Overwrite"))
445                         {
446                                 overwrite = (bool)values["Overwrite"];
447                         }
448
449                         try
450                         {
451                                 // Configure Attachment
452                                 FbConnectionStringBuilder csb = new FbConnectionStringBuilder();
453
454                                 csb.DataSource  = values["DataSource"].ToString();
455                                 csb.UserID              = values["User"].ToString();
456                                 csb.Password    = values["Password"].ToString();
457                                 csb.Database    = values["Database"].ToString();
458                                 csb.Port                = Convert.ToInt32(values["Port"], CultureInfo.InvariantCulture);
459                                 csb.ServerType  = serverType;
460
461                                 FbConnectionString options = new FbConnectionString(csb);
462
463                                 // DPB configuration
464                                 DatabaseParameterBuffer dpb = new DatabaseParameterBuffer();
465
466                                 // Dpb version
467                                 dpb.Append(IscCodes.isc_dpb_version1);
468
469                                 // Dummy packet interval
470                                 dpb.Append(IscCodes.isc_dpb_dummy_packet_interval, new byte[] { 120, 10, 0, 0 });
471
472                                 // User name
473                                 dpb.Append(IscCodes.isc_dpb_user_name, values["User"].ToString());
474
475                                 // User password
476                                 dpb.Append(IscCodes.isc_dpb_password, values["Password"].ToString());
477
478                                 // Database     dialect
479                                 dpb.Append(IscCodes.isc_dpb_sql_dialect, new byte[] { dialect, 0, 0, 0 });
480
481                                 // Database overwrite
482                                 dpb.Append(IscCodes.isc_dpb_overwrite, (short)(overwrite ? 1 : 0));
483
484                                 // Character set
485                                 if (values.ContainsKey("Charset"))
486                                 {
487                                         index = Charset.SupportedCharsets.IndexOf(values["Charset"].ToString());
488
489                                         if (index == -1)
490                                         {
491                                                 throw new ArgumentException("Character set is not valid.");
492                                         }
493                                         else
494                                         {
495                                                 dpb.Append(
496                                                         IscCodes.isc_dpb_set_db_charset,
497                                                         Charset.SupportedCharsets[index].Name);
498                                         }
499                                 }
500
501                                 // Page Size
502                                 if (values.ContainsKey("PageSize"))
503                                 {
504                                         dpb.Append(IscCodes.isc_dpb_page_size, Convert.ToInt32(values["PageSize"], CultureInfo.InvariantCulture));
505                                 }
506
507                                 // Forced writes
508                                 if (values.ContainsKey("ForcedWrite"))
509                                 {
510                                         dpb.Append(IscCodes.isc_dpb_force_write,
511                                                 (short)((bool)values["ForcedWrite"] ? 1 : 0));
512                                 }
513
514                                 if (!overwrite)
515                                 {
516                                         try
517                                         {
518                                                 // Check if     the     database exists
519                                                 FbConnectionInternal check = new FbConnectionInternal(options);
520
521                                                 check.Connect();
522                                                 check.Disconnect();
523
524                                                 IscException ex = new IscException(IscCodes.isc_db_or_file_exists);
525
526                                                 throw new FbException(ex.Message, ex);
527                                         }
528                                         catch (Exception)
529                                         {
530                                                 throw;
531                                         }
532                                 }
533
534                                 // Create the new database
535                                 FbConnectionInternal c = new FbConnectionInternal(options);
536                                 c.CreateDatabase(dpb);
537                         }
538                         catch (IscException ex)
539                         {
540                                 throw new FbException(ex.Message, ex);
541                         }
542                 }
543
544                 /// <include file='Doc/en_EN/FbConnection.xml' path='doc/class[@name="FbConnection"]/method[@name="DropDatabase(System.Collections.Hashtable)"]/*'/>
545                 [Obsolete("Use DropDatabase(string connectionString) instead")]
546                 public static void DropDatabase(Hashtable values)
547                 {
548                         int serverType = 0;
549
550                         if (!values.ContainsKey("User") ||
551                                 !values.ContainsKey("Password") ||
552                                 !values.ContainsKey("Database"))
553                         {
554                                 throw new ArgumentException("CreateDatabase requires a user name, password and database path.");
555                         }
556
557                         if (!values.ContainsKey("DataSource"))
558                         {
559                                 values.Add("DataSource", "localhost");
560                         }
561
562                         if (!values.ContainsKey("Port"))
563                         {
564                                 values.Add("Port", 3050);
565                         }
566
567                         if (values.ContainsKey("ServerType"))
568                         {
569                                 serverType = Convert.ToInt32(values["ServerType"], CultureInfo.InvariantCulture);
570                         }
571
572                         try
573                         {
574                                 // Configure Attachment
575                                 FbConnectionStringBuilder csb = new FbConnectionStringBuilder();
576
577                                 csb.DataSource = values["DataSource"].ToString();
578                                 csb.Port = Convert.ToInt32(values["Port"], CultureInfo.InvariantCulture);
579                                 csb.Database = values["Database"].ToString();
580                                 csb.UserID = values["User"].ToString();
581                                 csb.Password = values["Password"].ToString();
582                                 csb.ServerType = serverType;
583
584                                 FbConnectionString options = new FbConnectionString(csb);
585
586                                 // Drop the     database
587                                 FbConnectionInternal db = new FbConnectionInternal(options);
588                                 db.DropDatabase();
589                         }
590                         catch (IscException ex)
591                         {
592                                 throw new FbException(ex.Message, ex);
593                         }
594                 }
595
596                 #endregion
597
598                 #region Methods
599
600                 IDbTransaction IDbConnection.BeginTransaction()
601                 {
602                         return this.BeginTransaction();
603                 }
604
605                 IDbTransaction IDbConnection.BeginTransaction(IsolationLevel level)
606                 {
607                         return this.BeginTransaction(level);
608                 }
609
610                 /// <include file='Doc/en_EN/FbConnection.xml' path='doc/class[@name="FbConnection"]/method[@name="BeginTransaction"]/*'/>
611                 public FbTransaction BeginTransaction()
612                 {
613                         return this.BeginTransaction(IsolationLevel.ReadCommitted, null);
614                 }
615
616                 /// <include file='Doc/en_EN/FbConnection.xml' path='doc/class[@name="FbConnection"]/method[@name="BeginTransaction(System.String)"]/*'/>
617                 public FbTransaction BeginTransaction(string transactionName)
618                 {
619                         return this.BeginTransaction(IsolationLevel.ReadCommitted, transactionName);
620                 }
621
622                 /// <include file='Doc/en_EN/FbConnection.xml' path='doc/class[@name="FbConnection"]/method[@name="BeginTransaction(System.Data.IsolationLevel)"]/*'/>
623                 public FbTransaction BeginTransaction(IsolationLevel level)
624                 {
625                         return this.BeginTransaction(level, null);
626                 }
627
628                 /// <include file='Doc/en_EN/FbConnection.xml' path='doc/class[@name="FbConnection"]/method[@name="BeginTransaction(System.Data.IsolationLevel,System.String)"]/*'/>
629                 public FbTransaction BeginTransaction(IsolationLevel level, string transactionName)
630                 {
631                         if (this.IsClosed)
632                         {
633                                 throw new InvalidOperationException("BeginTransaction requires an open and available Connection.");
634                         }
635
636                         return this.innerConnection.BeginTransaction(level, transactionName);
637                 }
638
639                 /// <include file='Doc/en_EN/FbConnection.xml' path='doc/class[@name="FbConnection"]/method[@name="BeginTransaction(FbTransactionOptions)"]/*'/>
640                 public FbTransaction BeginTransaction(FbTransactionOptions options)
641                 {
642                         return this.BeginTransaction(options, null);
643                 }
644
645                 /// <include file='Doc/en_EN/FbConnection.xml' path='doc/class[@name="FbConnection"]/method[@name="BeginTransaction(FbTransactionOptions, System.String)"]/*'/>
646                 public FbTransaction BeginTransaction(FbTransactionOptions options, string transactionName)
647                 {
648                         if (this.IsClosed)
649                         {
650                                 throw new InvalidOperationException("BeginTransaction requires an open and available Connection.");
651                         }
652
653                         return this.innerConnection.BeginTransaction(options, transactionName);
654                 }
655
656                 /// <include file='Doc/en_EN/FbConnection.xml' path='doc/class[@name="FbConnection"]/method[@name="ChangeDatabase(System.String)"]/*'/>
657                 public void ChangeDatabase(string db)
658                 {
659                         lock (this)
660                         {
661                                 if (this.IsClosed)
662                                 {
663                                         throw new InvalidOperationException("ChangeDatabase requires an open and available Connection.");
664                                 }
665
666                                 if (db == null || db.Trim().Length == 0)
667                                 {
668                                         throw new InvalidOperationException("Database name is not valid.");
669                                 }
670
671                                 string cs = this.connectionString;
672
673                                 try
674                                 {
675                                         FbConnectionStringBuilder csb = new FbConnectionStringBuilder(this.connectionString);
676
677                                         // Close current connection
678                                         this.Close();
679
680                                         // Set up the new Database
681                                         csb.Database = db;
682
683                                         // Open new     connection
684                                         this.Open();
685                                 }
686                                 catch (IscException ex)
687                                 {
688                                         this.ConnectionString = cs;
689                                         throw new FbException(ex.Message, ex);
690                                 }
691                         }
692                 }
693
694                 /// <include file='Doc/en_EN/FbConnection.xml' path='doc/class[@name="FbConnection"]/method[@name="Open"]/*'/>
695                 public void Open()
696                 {
697                         lock (this)
698                         {
699                                 if (this.connectionString == null || this.connectionString.Length == 0)
700                                 {
701                                         throw new InvalidOperationException("Connection String is not initialized.");
702                                 }
703                                 if (!this.IsClosed && this.state != ConnectionState.Connecting)
704                                 {
705                                         throw new InvalidOperationException("Connection already Open.");
706                                 }
707
708                                 try
709                                 {
710                                         this.OnStateChange(this.state, ConnectionState.Connecting);
711
712                                         if (this.options.Pooling)
713                                         {
714                                                 // Use Connection Pooling
715                                                 FbConnectionPool pool = FbPoolManager.Instance.CreatePool(this.connectionString);
716                                                 this.innerConnection = pool.CheckOut();
717                                                 this.innerConnection.OwningConnection = this;
718                                         }
719                                         else
720                                         {
721                                                 // Do not use Connection Pooling
722                                                 this.innerConnection = new FbConnectionInternal(this.options, this);
723                                                 this.innerConnection.Connect();
724                                         }
725
726                                         // Bind Warning messages event
727                                         this.innerConnection.Database.WarningMessage = new WarningMessageCallback(this.OnWarningMessage);
728
729                                         // Update the connection state
730                                         this.OnStateChange(this.state, ConnectionState.Open);
731                                 }
732                                 catch (IscException ex)
733                                 {
734                                         this.OnStateChange(this.state, ConnectionState.Closed);
735                                         throw new FbException(ex.Message, ex);
736                                 }
737                                 catch (Exception)
738                                 {
739                                         this.OnStateChange(this.state, ConnectionState.Closed);
740                                         throw;
741                                 }
742                         }
743                 }
744
745                 /// <include file='Doc/en_EN/FbConnection.xml' path='doc/class[@name="FbConnection"]/method[@name="Close"]/*'/>
746                 public void Close()
747                 {
748                         if (this.IsClosed)
749                         {
750                                 return;
751                         }
752
753                         lock (this)
754                         {
755                                 try
756                                 {
757                                         lock (this.innerConnection)
758                                         {
759                                                 // Close the Remote     Event Manager
760                                                 this.innerConnection.CloseEventManager();
761
762                                                 // Unbind Warning messages event
763                                                 this.innerConnection.Database.WarningMessage = null;
764
765                                                 // Dispose Transaction
766                                                 this.innerConnection.DisposeTransaction();
767
768                                                 // Dispose all active statemenets
769                                                 this.innerConnection.DisposePreparedCommands();
770
771                                                 // Close connection     or send it back to the pool
772                                                 if (this.innerConnection.Pooled)
773                                                 {
774                                                         // Get Connection Pool
775                                                         FbConnectionPool pool = FbPoolManager.Instance.FindPool(this.connectionString);
776
777                                                         // Send connection to the Pool
778                                                         pool.CheckIn(this.innerConnection);
779                                                 }
780                                                 else
781                                                 {
782                                                         this.innerConnection.Disconnect();
783                                                 }
784                                         }
785
786                                         // Update connection state
787                                         this.OnStateChange(this.state, ConnectionState.Closed);
788                                 }
789                                 catch (IscException ex)
790                                 {
791                                         throw new FbException(ex.Message, ex);
792                                 }
793                         }
794                 }
795
796                 IDbCommand IDbConnection.CreateCommand()
797                 {
798                         return this.CreateCommand();
799                 }
800
801                 /// <include file='Doc/en_EN/FbConnection.xml' path='doc/class[@name="FbConnection"]/method[@name="CreateCommand"]/*'/>
802                 public FbCommand CreateCommand()
803                 {
804                         FbCommand command = new FbCommand();
805
806                         lock (this)
807                         {
808                                 command.Connection = this;
809                         }
810
811                         return command;
812                 }
813
814                 #endregion
815
816                 #region Database Schema
817
818                 /// <include file='Doc/en_EN/FbConnection.xml' path='doc/class[@name="FbConnection"]/method[@name="GetSchema"]/*'/>
819                 public DataTable GetSchema()
820                 {
821                         return this.GetSchema("MetaDataCollections");
822                 }
823
824                 /// <include file='Doc/en_EN/FbConnection.xml' path='doc/class[@name="FbConnection"]/method[@name="GetSchema(System.String)"]/*'/>
825                 public DataTable GetSchema(string collectionName)
826                 {
827                         return this.GetSchema(collectionName, null);
828                 }
829
830                 /// <include file='Doc/en_EN/FbConnection.xml' path='doc/class[@name="FbConnection"]/method[@name="GetSchema(System.String, System.String[])"]/*'/>
831                 public DataTable GetSchema(string collectionName, string[] restrictions)
832                 {
833                         if (this.IsClosed)
834                         {
835                                 throw new InvalidOperationException("The connection is closed.");
836                         }
837
838                         return this.innerConnection.GetSchema(collectionName, restrictions);
839                 }
840
841                 /// <include file='Doc/en_EN/FbConnection.xml' path='doc/class[@name="FbConnection"]/method[@name="GetDbSchemaTable"]/*'/>
842                 [Obsolete("Use GetSchema methods instead")]
843                 public DataTable GetDbSchemaTable(FbDbSchemaType schema, object[] restrictions)
844                 {
845                         if (this.state == ConnectionState.Closed)
846                         {
847                                 throw new InvalidOperationException("The conneciton is closed.");
848                         }
849
850                         return innerConnection.GetSchema(schema.ToString(), restrictions);
851                 }
852
853                 #endregion
854
855                 #region Private Methods
856
857                 private void OnWarningMessage(IscException warning)
858                 {
859                         if (this.InfoMessage != null)
860                         {
861                                 this.InfoMessage(this, new FbInfoMessageEventArgs(warning));
862                         }
863                 }
864
865                 private void OnStateChange(ConnectionState originalState, ConnectionState currentState)
866                 {
867                         this.state = currentState;
868                         if (this.StateChange != null)
869                         {
870                                 this.StateChange(this, new StateChangeEventArgs(originalState, currentState));
871                         }
872                 }
873
874                 #endregion
875         }
876 }