2008-11-05 Francisco Figueiredo Jr. <francisco@npgsql.org>
[mono.git] / mcs / class / Npgsql / Npgsql / NpgsqlConnection.cs
1 // created on 10/5/2002 at 23:01
2 // Npgsql.NpgsqlConnection.cs
3 //
4 // Author:
5 //      Francisco Jr. (fxjrlists@yahoo.com.br)
6 //
7 //      Copyright (C) 2002 The Npgsql Development Team
8 //      npgsql-general@gborg.postgresql.org
9 //      http://gborg.postgresql.org/project/npgsql/projdisplay.php
10 //
11 //
12 // Permission to use, copy, modify, and distribute this software and its
13 // documentation for any purpose, without fee, and without a written
14 // agreement is hereby granted, provided that the above copyright notice
15 // and this paragraph and the following two paragraphs appear in all copies.
16 // 
17 // IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY
18 // FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
19 // INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
20 // DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF
21 // THE POSSIBILITY OF SUCH DAMAGE.
22 // 
23 // THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES,
24 // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
25 // AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
26 // ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS
27 // TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
28
29 using System;
30 using System.ComponentModel;
31 using System.Data;
32 using System.Data.Common;
33 using System.Resources;
34 using System.Security.Cryptography;
35 using System.Security.Cryptography.X509Certificates;
36 using System.Transactions;
37 using Mono.Security.Protocol.Tls;
38 using IsolationLevel = System.Data.IsolationLevel;
39
40 #if WITHDESIGN
41
42 #endif
43
44 namespace Npgsql
45 {
46         /// <summary>
47         /// Represents the method that handles the <see cref="Npgsql.NpgsqlConnection.Notification">Notice</see> events.
48         /// </summary>
49         /// <param name="e">A <see cref="Npgsql.NpgsqlNoticeEventArgs">NpgsqlNoticeEventArgs</see> that contains the event data.</param>
50         public delegate void NoticeEventHandler(Object sender, NpgsqlNoticeEventArgs e);
51
52         /// <summary>
53         /// Represents the method that handles the <see cref="Npgsql.NpgsqlConnection.Notification">Notification</see> events.
54         /// </summary>
55         /// <param name="sender">The source of the event.</param>
56         /// <param name="e">A <see cref="Npgsql.NpgsqlNotificationEventArgs">NpgsqlNotificationEventArgs</see> that contains the event data.</param>
57         public delegate void NotificationEventHandler(Object sender, NpgsqlNotificationEventArgs e);
58
59         /// <summary>
60         /// This class represents a connection to a
61         /// PostgreSQL server.
62         /// </summary>
63 #if WITHDESIGN
64     [System.Drawing.ToolboxBitmapAttribute(typeof(NpgsqlConnection))]
65 #endif
66
67         public sealed class NpgsqlConnection : DbConnection, ICloneable
68         {
69                 // Logging related values
70                 private static readonly String CLASSNAME = "NpgsqlConnection";
71                 private static readonly ResourceManager resman = new ResourceManager(typeof(NpgsqlConnection));
72
73                 // Parsed connection string cache
74                 private static readonly Cache<NpgsqlConnectionStringBuilder> cache = new Cache<NpgsqlConnectionStringBuilder>();
75
76                 /// <summary>
77                 /// Occurs on NoticeResponses from the PostgreSQL backend.
78                 /// </summary>
79                 public event NoticeEventHandler Notice;
80
81                 internal NoticeEventHandler NoticeDelegate;
82
83                 /// <summary>
84                 /// Occurs on NotificationResponses from the PostgreSQL backend.
85                 /// </summary>
86                 public event NotificationEventHandler Notification;
87
88                 internal NotificationEventHandler NotificationDelegate;
89
90                 /// <summary>
91                 /// Mono.Security.Protocol.Tls.CertificateSelectionCallback delegate.
92                 /// </summary>
93                 public event CertificateSelectionCallback CertificateSelectionCallback;
94
95                 internal CertificateSelectionCallback CertificateSelectionCallbackDelegate;
96
97                 /// <summary>
98                 /// Mono.Security.Protocol.Tls.CertificateValidationCallback delegate.
99                 /// </summary>
100                 public event CertificateValidationCallback CertificateValidationCallback;
101
102                 internal CertificateValidationCallback CertificateValidationCallbackDelegate;
103
104                 /// <summary>
105                 /// Mono.Security.Protocol.Tls.PrivateKeySelectionCallback delegate.
106                 /// </summary>
107                 public event PrivateKeySelectionCallback PrivateKeySelectionCallback;
108
109                 internal PrivateKeySelectionCallback PrivateKeySelectionCallbackDelegate;
110
111                 // Set this when disposed is called.
112                 private bool disposed = false;
113
114                 // Used when we closed the connector due to an error, but are pretending it's open.
115                 private bool _fakingOpen;
116
117                 // Strong-typed ConnectionString values
118                 private NpgsqlConnectionStringBuilder settings;
119
120                 // Connector being used for the active connection.
121                 private NpgsqlConnector connector = null;
122
123                 private readonly NpgsqlPromotableSinglePhaseNotification promotable = null;
124
125                 /// <summary>
126                 /// Initializes a new instance of the
127                 /// <see cref="Npgsql.NpgsqlConnection">NpgsqlConnection</see> class.
128                 /// </summary>
129                 public NpgsqlConnection()
130                         : this(String.Empty)
131                 {
132                 }
133
134                 /// <summary>
135                 /// Initializes a new instance of the
136                 /// <see cref="Npgsql.NpgsqlConnection">NpgsqlConnection</see> class
137                 /// and sets the <see cref="Npgsql.NpgsqlConnection.ConnectionString">ConnectionString</see>.
138                 /// </summary>
139                 /// <param name="ConnectionString">The connection used to open the PostgreSQL database.</param>
140                 public NpgsqlConnection(String ConnectionString)
141                 {
142                         NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, CLASSNAME, "NpgsqlConnection()");
143
144                         NpgsqlConnectionStringBuilder builder = cache[ConnectionString];
145                         if (builder == null)
146                         {
147                                 settings = new NpgsqlConnectionStringBuilder(ConnectionString);
148                         }
149                         else
150                         {
151                                 settings = builder.Clone();
152                         }
153
154                         LogConnectionString();
155
156                         NoticeDelegate = new NoticeEventHandler(OnNotice);
157                         NotificationDelegate = new NotificationEventHandler(OnNotification);
158
159                         CertificateValidationCallbackDelegate = new CertificateValidationCallback(DefaultCertificateValidationCallback);
160                         CertificateSelectionCallbackDelegate = new CertificateSelectionCallback(DefaultCertificateSelectionCallback);
161                         PrivateKeySelectionCallbackDelegate = new PrivateKeySelectionCallback(DefaultPrivateKeySelectionCallback);
162
163                         // Fix authentication problems. See https://bugzilla.novell.com/show_bug.cgi?id=MONO77559 and 
164                         // http://pgfoundry.org/forum/message.php?msg_id=1002377 for more info.
165                         RSACryptoServiceProvider.UseMachineKeyStore = true;
166
167                         promotable = new NpgsqlPromotableSinglePhaseNotification(this);
168                 }
169
170                 /// <summary>
171                 /// Gets or sets the string used to connect to a PostgreSQL database.
172                 /// Valid values are:
173                 /// <ul>
174                 /// <li>
175                 /// Server:             Address/Name of Postgresql Server;
176                 /// </li>
177                 /// <li>
178                 /// Port:               Port to connect to;
179                 /// </li>
180                 /// <li>
181                 /// Protocol:           Protocol version to use, instead of automatic; Integer 2 or 3;
182                 /// </li>
183                 /// <li>
184                 /// Database:           Database name. Defaults to user name if not specified;
185                 /// </li>
186                 /// <li>
187                 /// User Id:            User name;
188                 /// </li>
189                 /// <li>
190                 /// Password:           Password for clear text authentication;
191                 /// </li>
192                 /// <li>
193                 /// SSL:                True or False. Controls whether to attempt a secure connection. Default = False;
194                 /// </li>
195                 /// <li>
196                 /// Pooling:            True or False. Controls whether connection pooling is used. Default = True;
197                 /// </li>
198                 /// <li>
199                 /// MinPoolSize:        Min size of connection pool;
200                 /// </li>
201                 /// <li>
202                 /// MaxPoolSize:        Max size of connection pool;
203                 /// </li>
204                 /// <li>
205                 /// Timeout:            Time to wait for connection open in seconds. Default is 15.
206                 /// </li>
207                 /// <li>
208                 /// CommandTimeout:     Time to wait for command to finish execution before throw an exception. In seconds. Default is 20.
209                 /// </li>
210                 /// <li>
211                 /// Sslmode:            Mode for ssl connection control. Can be Prefer, Require, Allow or Disable. Default is Disable. Check user manual for explanation of values.
212                 /// </li>
213                 /// <li>
214                 /// ConnectionLifeTime: Time to wait before closing unused connections in the pool in seconds. Default is 15.
215                 /// </li>
216                 /// <li>
217                 /// SyncNotification:   Specifies if Npgsql should use synchronous notifications.
218                 /// </li>
219                 /// <li>
220                 /// SearchPath: Changes search path to specified and public schemas.
221                 /// </li>
222                 /// </ul>
223                 /// </summary>
224                 /// <value>The connection string that includes the server name,
225                 /// the database name, and other parameters needed to establish
226                 /// the initial connection. The default value is an empty string.
227                 /// </value>
228
229 #if WITHDESIGN
230         [RefreshProperties(RefreshProperties.All), DefaultValue(""), RecommendedAsConfigurable(true)]
231         [NpgsqlSysDescription("Description_ConnectionString", typeof(NpgsqlConnection)), Category("Data")]
232         [Editor(typeof(ConnectionStringEditor), typeof(System.Drawing.Design.UITypeEditor))]
233 #endif
234
235                 public override String ConnectionString
236                 {
237                         get { return settings.ConnectionString; }
238                         set
239                         {
240                                 // Connection string is used as the key to the connector.  Because of this,
241                                 // we cannot change it while we own a connector.
242                                 CheckConnectionClosed();
243                                 NpgsqlEventLog.LogPropertySet(LogLevel.Debug, CLASSNAME, "ConnectionString", value);
244                                 NpgsqlConnectionStringBuilder builder = cache[value];
245                                 if (builder == null)
246                                 {
247                                         settings = new NpgsqlConnectionStringBuilder(value);
248                                 }
249                                 else
250                                 {
251                                         settings = builder.Clone();
252                                 }
253                                 LogConnectionString();
254                         }
255                 }
256
257                 /// <summary>
258                 /// Backend server host name.
259                 /// </summary>
260                 [Browsable(true)]
261                 public String Host
262                 {
263                         get { return settings.Host; }
264                 }
265
266                 /// <summary>
267                 /// Backend server port.
268                 /// </summary>
269                 [Browsable(true)]
270                 public Int32 Port
271                 {
272                         get { return settings.Port; }
273                 }
274
275                 /// <summary>
276                 /// If true, the connection will attempt to use SSL.
277                 /// </summary>
278                 [Browsable(true)]
279                 public Boolean SSL
280                 {
281                         get { return settings.SSL; }
282                 }
283
284                 /// <summary>
285                 /// Gets the time to wait while trying to establish a connection
286                 /// before terminating the attempt and generating an error.
287                 /// </summary>
288                 /// <value>The time (in seconds) to wait for a connection to open. The default value is 15 seconds.</value>
289
290 #if WITHDESIGN
291         [NpgsqlSysDescription("Description_ConnectionTimeout", typeof(NpgsqlConnection))]
292 #endif
293
294                 public override Int32 ConnectionTimeout
295                 {
296                         get { return settings.Timeout; }
297                 }
298
299                 /// <summary>
300                 /// Gets the time to wait while trying to execute a command
301                 /// before terminating the attempt and generating an error.
302                 /// </summary>
303                 /// <value>The time (in seconds) to wait for a command to complete. The default value is 20 seconds.</value>
304                 public Int32 CommandTimeout
305                 {
306                         get { return settings.CommandTimeout; }
307                 }
308
309                 /// <summary>
310                 /// Gets the time to wait before closing unused connections in the pool if the count
311                 /// of all connections exeeds MinPoolSize.
312                 /// </summary>
313                 /// <remarks>
314                 /// If connection pool contains unused connections for ConnectionLifeTime seconds,
315                 /// the half of them will be closed. If there will be unused connections in a second
316                 /// later then again the half of them will be closed and so on.
317                 /// This strategy provide smooth change of connection count in the pool.
318                 /// </remarks>
319                 /// <value>The time (in seconds) to wait. The default value is 15 seconds.</value>
320                 public Int32 ConnectionLifeTime
321                 {
322                         get { return settings.ConnectionLifeTime; }
323                 }
324
325                 ///<summary>
326                 /// Gets the name of the current database or the database to be used after a connection is opened.
327                 /// </summary>
328                 /// <value>The name of the current database or the name of the database to be
329                 /// used after a connection is opened. The default value is the empty string.</value>
330 #if WITHDESIGN
331         [NpgsqlSysDescription("Description_Database", typeof(NpgsqlConnection))]
332 #endif
333
334                 public override String Database
335                 {
336                         get { return settings.Database; }
337                 }
338
339                 /// <summary>
340                 /// Whether datareaders are loaded in their entirety (for compatibility with earlier code).
341                 /// </summary>
342                 public bool PreloadReader
343                 {
344                         get { return settings.PreloadReader; }
345                 }
346
347                 /// <summary>
348                 /// Gets the database server name.
349                 /// </summary>
350                 public override string DataSource
351                 {
352                         get { return settings.Host; }
353                 }
354
355                 /// <summary>
356                 /// Gets flag indicating if we are using Synchronous notification or not.
357                 /// The default value is false.
358                 /// </summary>
359                 public Boolean SyncNotification
360                 {
361                         get { return settings.SyncNotification; }
362                 }
363
364                 /// <summary>
365                 /// Gets the current state of the connection.
366                 /// </summary>
367                 /// <value>A bitwise combination of the <see cref="System.Data.ConnectionState">ConnectionState</see> values. The default is <b>Closed</b>.</value>
368                 [Browsable(false)]
369                 public ConnectionState FullState
370                 {
371                         get
372                         {
373                                 CheckNotDisposed();
374
375                                 if (connector != null)
376                                 {
377                                         return connector.State;
378                                 }
379                                 else
380                                 {
381                                         return ConnectionState.Closed;
382                                 }
383                         }
384                 }
385
386                 /// <summary>
387                 /// Gets whether the current state of the connection is Open or Closed
388                 /// </summary>
389                 /// <value>ConnectionState.Open or ConnectionState.Closed</value>
390                 [Browsable(false)]
391         public override ConnectionState State
392         {
393             get
394             {
395                 return (FullState & ConnectionState.Open) == ConnectionState.Open ? ConnectionState.Open : ConnectionState.Closed;
396             }
397         }
398
399         /// <summary>
400                 /// Version of the PostgreSQL backend.
401                 /// This can only be called when there is an active connection.
402                 /// </summary>
403                 [Browsable(false)]
404                 public Version PostgreSqlVersion
405                 {
406                         get
407                         {
408                                 CheckConnectionOpen();
409                                 return connector.ServerVersion;
410                         }
411                 }
412
413                 public override string ServerVersion
414                 {
415                         get { return PostgreSqlVersion.ToString(); }
416                 }
417
418                 /// <summary>
419                 /// Protocol version in use.
420                 /// This can only be called when there is an active connection.
421                 /// </summary>
422                 [Browsable(false)]
423                 public ProtocolVersion BackendProtocolVersion
424                 {
425                         get
426                         {
427                                 CheckConnectionOpen();
428                                 return connector.BackendProtocolVersion;
429                         }
430                 }
431
432                 /// <summary>
433                 /// Process id of backend server.
434                 /// This can only be called when there is an active connection.
435                 /// </summary>
436                 [Browsable(false)]
437                 public Int32 ProcessID
438                 {
439                         get
440                         {
441                                 CheckConnectionOpen();
442                                 return connector.BackEndKeyData.ProcessID;
443                         }
444                 }
445
446                 /// <summary>
447                 /// Begins a database transaction with the specified isolation level.
448                 /// </summary>
449                 /// <param name="isolationLevel">The <see cref="System.Data.IsolationLevel">isolation level</see> under which the transaction should run.</param>
450                 /// <returns>An <see cref="System.Data.Common.DbTransaction">DbTransaction</see>
451                 /// object representing the new transaction.</returns>
452                 /// <remarks>
453                 /// Currently the IsolationLevel ReadCommitted and Serializable are supported by the PostgreSQL backend.
454                 /// There's no support for nested transactions.
455                 /// </remarks>
456                 protected override DbTransaction BeginDbTransaction(IsolationLevel isolationLevel)
457                 {
458                         NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "BeginDbTransaction", isolationLevel);
459
460                         return BeginTransaction(isolationLevel);
461                 }
462
463                 /// <summary>
464                 /// Begins a database transaction.
465                 /// </summary>
466                 /// <returns>A <see cref="Npgsql.NpgsqlTransaction">NpgsqlTransaction</see>
467                 /// object representing the new transaction.</returns>
468                 /// <remarks>
469                 /// Currently there's no support for nested transactions.
470                 /// </remarks>
471                 public new NpgsqlTransaction BeginTransaction()
472                 {
473                         NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "BeginTransaction");
474                         return this.BeginTransaction(IsolationLevel.ReadCommitted);
475                 }
476
477                 /// <summary>
478                 /// Begins a database transaction with the specified isolation level.
479                 /// </summary>
480                 /// <param name="level">The <see cref="System.Data.IsolationLevel">isolation level</see> under which the transaction should run.</param>
481                 /// <returns>A <see cref="Npgsql.NpgsqlTransaction">NpgsqlTransaction</see>
482                 /// object representing the new transaction.</returns>
483                 /// <remarks>
484                 /// Currently the IsolationLevel ReadCommitted and Serializable are supported by the PostgreSQL backend.
485                 /// There's no support for nested transactions.
486                 /// </remarks>
487                 public new NpgsqlTransaction BeginTransaction(IsolationLevel level)
488                 {
489                         NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "BeginTransaction", level);
490
491                         CheckConnectionOpen();
492
493                         if (connector.Transaction != null)
494                         {
495                                 throw new InvalidOperationException(resman.GetString("Exception_NoNestedTransactions"));
496                         }
497
498                         return new NpgsqlTransaction(this, level);
499                 }
500
501                 /// <summary>
502                 /// Opens a database connection with the property settings specified by the
503                 /// <see cref="Npgsql.NpgsqlConnection.ConnectionString">ConnectionString</see>.
504                 /// </summary>
505                 public override void Open()
506                 {
507                         CheckConnectionClosed();
508
509                         NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Open");
510
511                         // Check if there is any missing argument.
512                         if (!settings.ContainsKey(Keywords.Host))
513                         {
514                                 throw new ArgumentException(resman.GetString("Exception_MissingConnStrArg"),
515                                                                                         NpgsqlConnectionStringBuilder.GetKeyName(Keywords.Host));
516                         }
517                         if (!settings.ContainsKey(Keywords.UserName) && !settings.ContainsKey(Keywords.IntegratedSecurity))
518                         {
519                                 throw new ArgumentException(resman.GetString("Exception_MissingConnStrArg"),
520                                                                                         NpgsqlConnectionStringBuilder.GetKeyName(Keywords.UserName));
521                         }
522
523                         // Get a Connector.  The connector returned is guaranteed to be connected and ready to go.
524                         connector = NpgsqlConnectorPool.ConnectorPoolMgr.RequestConnector(this);
525
526                         connector.Notice += NoticeDelegate;
527                         connector.Notification += NotificationDelegate;
528
529                         if (SyncNotification)
530                         {
531                                 connector.AddNotificationThread();
532                         }
533
534                         if (Enlist)
535                         {
536                                 promotable.Enlist(Transaction.Current);
537                         }
538                 }
539
540                 /// <summary>
541                 /// This method changes the current database by disconnecting from the actual
542                 /// database and connecting to the specified.
543                 /// </summary>
544                 /// <param name="dbName">The name of the database to use in place of the current database.</param>
545                 public override void ChangeDatabase(String dbName)
546                 {
547                         CheckNotDisposed();
548
549                         NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ChangeDatabase", dbName);
550
551                         if (dbName == null)
552                         {
553                                 throw new ArgumentNullException("dbName");
554                         }
555
556                         if (string.IsNullOrEmpty(dbName))
557                         {
558                                 throw new ArgumentOutOfRangeException(String.Format(resman.GetString("Exception_InvalidDbName"), dbName), "dbName");
559                         }
560
561                         String oldDatabaseName = Database;
562
563                         Close();
564
565                         settings[Keywords.Database] = dbName;
566
567                         Open();
568                 }
569
570                 internal void EmergencyClose()
571                 {
572                         _fakingOpen = true;
573                 }
574
575                 /// <summary>
576                 /// Releases the connection to the database.  If the connection is pooled, it will be
577                 ///     made available for re-use.  If it is non-pooled, the actual connection will be shutdown.
578                 /// </summary>
579                 public override void Close()
580                 {
581                         if (!disposed)
582                         {
583                                 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Close");
584
585                                 promotable.Prepare();
586
587                                 if (connector != null)
588                                 {
589                                         connector.Notification -= NotificationDelegate;
590                                         connector.Notice -= NoticeDelegate;
591
592                                         if (SyncNotification)
593                                         {
594                                                 connector.RemoveNotificationThread();
595                                         }
596
597                                         NpgsqlConnectorPool.ConnectorPoolMgr.ReleaseConnector(this, connector);
598
599
600                                         connector = null;
601                                 }
602                         }
603                 }
604
605                 /// <summary>
606                 /// Creates and returns a <see cref="System.Data.Common.DbCommand">DbCommand</see>
607                 /// object associated with the <see cref="System.Data.Common.DbConnection">IDbConnection</see>.
608                 /// </summary>
609                 /// <returns>A <see cref="System.Data.Common.DbCommand">DbCommand</see> object.</returns>
610                 protected override DbCommand CreateDbCommand()
611                 {
612                         NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "CreateDbCommand");
613                         return CreateCommand();
614                 }
615
616                 /// <summary>
617                 /// Creates and returns a <see cref="Npgsql.NpgsqlCommand">NpgsqlCommand</see>
618                 /// object associated with the <see cref="Npgsql.NpgsqlConnection">NpgsqlConnection</see>.
619                 /// </summary>
620                 /// <returns>A <see cref="Npgsql.NpgsqlCommand">NpgsqlCommand</see> object.</returns>
621                 public new NpgsqlCommand CreateCommand()
622                 {
623                         CheckNotDisposed();
624
625                         NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "CreateCommand");
626                         return new NpgsqlCommand("", this);
627                 }
628
629                 /// <summary>
630                 /// Releases all resources used by the
631                 /// <see cref="Npgsql.NpgsqlConnection">NpgsqlConnection</see>.
632                 /// </summary>
633                 /// <param name="disposing"><b>true</b> when called from Dispose();
634                 /// <b>false</b> when being called from the finalizer.</param>
635                 protected override void Dispose(bool disposing)
636                 {
637                         if (!disposed)
638                         {
639                                 if (disposing)
640                                 {
641                                         NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Dispose");
642                                         Close();
643                                 }
644                                 else
645                                 {
646                                         if (FullState != ConnectionState.Closed)
647                                         {
648                                                 NpgsqlEventLog.LogMsg(resman, "Log_ConnectionLeaking", LogLevel.Debug);
649                                                 NpgsqlConnectorPool.ConnectorPoolMgr.FixPoolCountBecauseOfConnectionDisposeFalse(this);
650                                         }
651                                 }
652
653                                 base.Dispose(disposing);
654                                 disposed = true;
655                         }
656                 }
657
658                 /// <summary>
659                 /// Create a new connection based on this one.
660                 /// </summary>
661                 /// <returns>A new NpgsqlConnection object.</returns>
662                 Object ICloneable.Clone()
663                 {
664                         return Clone();
665                 }
666
667                 /// <summary>
668                 /// Create a new connection based on this one.
669                 /// </summary>
670                 /// <returns>A new NpgsqlConnection object.</returns>
671                 public NpgsqlConnection Clone()
672                 {
673                         CheckNotDisposed();
674
675                         NpgsqlConnection C = new NpgsqlConnection(ConnectionString);
676
677                         C.Notice += this.Notice;
678
679                         if (connector != null)
680                         {
681                                 C.Open();
682                         }
683
684                         return C;
685                 }
686
687                 //
688                 // Internal methods and properties
689                 //
690                 internal void OnNotice(object O, NpgsqlNoticeEventArgs E)
691                 {
692                         if (Notice != null)
693                         {
694                                 Notice(this, E);
695                         }
696                 }
697
698                 internal void OnNotification(object O, NpgsqlNotificationEventArgs E)
699                 {
700                         if (Notification != null)
701                         {
702                                 Notification(this, E);
703                         }
704                 }
705
706                 /// <summary>
707                 /// The connector object connected to the backend.
708                 /// </summary>
709                 internal NpgsqlConnector Connector
710                 {
711                         get { return connector; }
712                 }
713
714
715                 /// <summary>
716                 /// Gets the NpgsqlConnectionStringBuilder containing the parsed connection string values.
717                 /// </summary>
718                 internal NpgsqlConnectionStringBuilder ConnectionStringValues
719                 {
720                         get { return settings; }
721                 }
722
723                 /// <summary>
724                 /// User name.
725                 /// </summary>
726                 internal String UserName
727                 {
728                         get { return settings.UserName; }
729                 }
730
731                 public bool UseExtendedTypes
732                 {
733                         get
734                         {
735                                 bool ext = settings.UseExtendedTypes;
736                                 return ext;
737                         }
738                 }
739
740                 /// <summary>
741                 /// Password.
742                 /// </summary>
743                 internal String Password
744                 {
745                         get { return settings.Password; }
746                 }
747
748                 /// <summary>
749                 /// Determine if connection pooling will be used for this connection.
750                 /// </summary>
751                 internal Boolean Pooling
752                 {
753                         get { return (settings.Pooling && (settings.MaxPoolSize > 0)); }
754                 }
755
756                 internal Int32 MinPoolSize
757                 {
758                         get { return settings.MinPoolSize; }
759                 }
760
761                 internal Int32 MaxPoolSize
762                 {
763                         get { return settings.MaxPoolSize; }
764                 }
765
766                 internal Int32 Timeout
767                 {
768                         get { return settings.Timeout; }
769                 }
770
771                 internal Boolean Enlist
772                 {
773                         get { return settings.Enlist; }
774                 }
775
776
777                 //
778                 // Event handlers
779                 //
780
781                 /// <summary>
782                 /// Default SSL CertificateSelectionCallback implementation.
783                 /// </summary>
784                 internal X509Certificate DefaultCertificateSelectionCallback(X509CertificateCollection clientCertificates,
785                                                                                                                                          X509Certificate serverCertificate, string targetHost,
786                                                                              X509CertificateCollection serverRequestedCertificates)
787                 {
788                         if (CertificateSelectionCallback != null)
789                         {
790                                 return CertificateSelectionCallback(clientCertificates, serverCertificate, targetHost, serverRequestedCertificates);
791                         }
792                         else
793                         {
794                                 return null;
795                         }
796                 }
797
798                 /// <summary>
799                 /// Default SSL CertificateValidationCallback implementation.
800                 /// </summary>
801                 internal bool DefaultCertificateValidationCallback(X509Certificate certificate, int[] certificateErrors)
802                 {
803                         if (CertificateValidationCallback != null)
804                         {
805                                 return CertificateValidationCallback(certificate, certificateErrors);
806                         }
807                         else
808                         {
809                                 return true;
810                         }
811                 }
812
813                 /// <summary>
814                 /// Default SSL PrivateKeySelectionCallback implementation.
815                 /// </summary>
816                 internal AsymmetricAlgorithm DefaultPrivateKeySelectionCallback(X509Certificate certificate, string targetHost)
817                 {
818                         if (PrivateKeySelectionCallback != null)
819                         {
820                                 return PrivateKeySelectionCallback(certificate, targetHost);
821                         }
822                         else
823                         {
824                                 return null;
825                         }
826                 }
827
828
829                 //
830                 // Private methods and properties
831                 //
832
833
834                 /// <summary>
835                 /// Write each key/value pair in the connection string to the log.
836                 /// </summary>
837                 private void LogConnectionString()
838                 {
839                         foreach (string key in settings.Keys)
840                         {
841                                 NpgsqlEventLog.LogMsg(resman, "Log_ConnectionStringValues", LogLevel.Debug, key, settings[key]);
842                         }
843                 }
844
845                 private void CheckConnectionOpen()
846                 {
847                         if (disposed)
848                         {
849                                 throw new ObjectDisposedException(CLASSNAME);
850                         }
851
852                         if (_fakingOpen)
853                         {
854                                 if (connector != null)
855                                 {
856                                         try
857                                         {
858                                                 Close();
859                                         }
860                                         catch
861                                         {
862                                         }
863                                 }
864                                 Open();
865                                 _fakingOpen = false;
866                         }
867
868                         if (connector == null)
869                         {
870                                 throw new InvalidOperationException(resman.GetString("Exception_ConnNotOpen"));
871                         }
872                 }
873
874                 private void CheckConnectionClosed()
875                 {
876                         if (disposed)
877                         {
878                                 throw new ObjectDisposedException(CLASSNAME);
879                         }
880
881                         if (connector != null)
882                         {
883                                 throw new InvalidOperationException(resman.GetString("Exception_ConnOpen"));
884                         }
885                 }
886
887                 private void CheckNotDisposed()
888                 {
889                         if (disposed)
890                         {
891                                 throw new ObjectDisposedException(CLASSNAME);
892                         }
893                 }
894
895
896                 /// <summary>
897                 /// Returns the supported collections
898                 /// </summary>
899                 public override DataTable GetSchema()
900                 {
901                         return NpgsqlSchema.GetMetaDataCollections();
902                 }
903
904                 /// <summary>
905                 /// Returns the schema collection specified by the collection name.
906                 /// </summary>
907                 /// <param name="collectionName">The collection name.</param>
908                 /// <returns>The collection specified.</returns>
909                 public override DataTable GetSchema(string collectionName)
910                 {
911                         return GetSchema(collectionName, null);
912                 }
913
914                 /// <summary>
915                 /// Returns the schema collection specified by the collection name filtered by the restrictions.
916                 /// </summary>
917                 /// <param name="collectionName">The collection name.</param>
918                 /// <param name="restrictions">
919                 /// The restriction values to filter the results.  A description of the restrictions is contained
920                 /// in the Restrictions collection.
921                 /// </param>
922                 /// <returns>The collection specified.</returns>
923                 public override DataTable GetSchema(string collectionName, string[] restrictions)
924                 {
925                         switch (collectionName)
926                         {
927                                 case "MetaDataCollections":
928                                         return NpgsqlSchema.GetMetaDataCollections();
929                                 case "Restrictions":
930                                         return NpgsqlSchema.GetRestrictions();
931                                 case "DataSourceInformation":
932                                         return NpgsqlSchema.GetDataSourceInformation();
933                                 case "DataTypes":
934                                 case "ReservedWords":
935                                         throw new NotSupportedException();
936                                 // custom collections for npgsql
937                                 case "Databases":
938                                         return new NpgsqlSchema(new NpgsqlConnection(ConnectionString)).GetDatabases(restrictions);
939                                 case "Tables":
940                                         return new NpgsqlSchema(new NpgsqlConnection(ConnectionString)).GetTables(restrictions);
941                                 case "Columns":
942                                         return new NpgsqlSchema(new NpgsqlConnection(ConnectionString)).GetColumns(restrictions);
943                                 case "Views":
944                                         return new NpgsqlSchema(new NpgsqlConnection(ConnectionString)).GetViews(restrictions);
945                                 case "Users":
946                                         return new NpgsqlSchema(new NpgsqlConnection(ConnectionString)).GetUsers(restrictions);
947                                 default:
948                                         throw new NotSupportedException();
949                         }
950                 }
951
952                 public void ClearPool()
953                 {
954                         NpgsqlConnectorPool.ConnectorPoolMgr.ClearPool(this);
955                 }
956
957                 public static void ClearAllPools()
958                 {
959                         NpgsqlConnectorPool.ConnectorPoolMgr.ClearAllPools();
960                 }
961
962                 public override void EnlistTransaction(Transaction transaction)
963                 {
964                         promotable.Enlist(transaction);
965                 }
966
967 #if NET35
968         protected override DbProviderFactory DbProviderFactory
969         {
970             get
971             {
972                 return NpgsqlFactory.Instance;
973             }
974         }
975 #endif
976         }
977 }