1 // created on 10/5/2002 at 23:01
2 // Npgsql.NpgsqlConnection.cs
5 // Francisco Jr. (fxjrlists@yahoo.com.br)
7 // Copyright (C) 2002 The Npgsql Development Team
8 // npgsql-general@gborg.postgresql.org
9 // http://gborg.postgresql.org/project/npgsql/projdisplay.php
12 // This library is free software; you can redistribute it and/or
13 // modify it under the terms of the GNU Lesser General Public
14 // License as published by the Free Software Foundation; either
15 // version 2.1 of the License, or (at your option) any later version.
17 // This library is distributed in the hope that it will be useful,
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 // Lesser General Public License for more details.
22 // You should have received a copy of the GNU Lesser General Public
23 // License along with this library; if not, write to the Free Software
24 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 using System.ComponentModel;
30 using System.Collections;
31 using System.Resources;
32 using System.Security.Cryptography;
33 using System.Security.Cryptography.X509Certificates;
35 using Mono.Security.Protocol.Tls;
46 /// Represents the method that handles the <see cref="Npgsql.NpgsqlConnection.Notification">Notice</see> events.
48 /// <param name="e">A <see cref="Npgsql.NpgsqlNoticeEventArgs">NpgsqlNoticeEventArgs</see> that contains the event data.</param>
49 public delegate void NoticeEventHandler(Object sender, NpgsqlNoticeEventArgs e);
52 /// Represents the method that handles the <see cref="Npgsql.NpgsqlConnection.Notification">Notification</see> events.
54 /// <param name="sender">The source of the event.</param>
55 /// <param name="e">A <see cref="Npgsql.NpgsqlNotificationEventArgs">NpgsqlNotificationEventArgs</see> that contains the event data.</param>
56 public delegate void NotificationEventHandler(Object sender, NpgsqlNotificationEventArgs e);
59 /// This class represents a connection to a
60 /// PostgreSQL server.
63 [System.Drawing.ToolboxBitmapAttribute(typeof(NpgsqlConnection))]
66 public sealed class NpgsqlConnection : Component, IDbConnection, ICloneable
68 // Logging related values
69 private static readonly String CLASSNAME = "NpgsqlConnection";
70 private static ResourceManager resman = new System.Resources.ResourceManager(typeof(NpgsqlConnection));
73 /// Occurs on NoticeResponses from the PostgreSQL backend.
75 public event NoticeEventHandler Notice;
76 internal NoticeEventHandler NoticeDelegate;
79 /// Occurs on NotificationResponses from the PostgreSQL backend.
81 public event NotificationEventHandler Notification;
82 internal NotificationEventHandler NotificationDelegate;
85 /// Mono.Security.Protocol.Tls.CertificateSelectionCallback delegate.
87 public event CertificateSelectionCallback CertificateSelectionCallback;
88 internal CertificateSelectionCallback CertificateSelectionCallbackDelegate;
91 /// Mono.Security.Protocol.Tls.CertificateValidationCallback delegate.
93 public event CertificateValidationCallback CertificateValidationCallback;
94 internal CertificateValidationCallback CertificateValidationCallbackDelegate;
97 /// Mono.Security.Protocol.Tls.PrivateKeySelectionCallback delegate.
99 public event PrivateKeySelectionCallback PrivateKeySelectionCallback;
100 internal PrivateKeySelectionCallback PrivateKeySelectionCallbackDelegate;
102 // Set this when disposed is called.
103 private bool disposed = false;
105 // Connection string values.
106 private NpgsqlConnectionString connection_string;
108 // Connector being used for the active connection.
109 private NpgsqlConnector connector = null;
113 /// Initializes a new instance of the
114 /// <see cref="Npgsql.NpgsqlConnection">NpgsqlConnection</see> class.
116 public NpgsqlConnection() : this(String.Empty)
120 /// Initializes a new instance of the
121 /// <see cref="Npgsql.NpgsqlConnection">NpgsqlConnection</see> class
122 /// and sets the <see cref="Npgsql.NpgsqlConnection.ConnectionString">ConnectionString</see>.
124 /// <param name="ConnectionString">The connection used to open the PostgreSQL database.</param>
125 public NpgsqlConnection(String ConnectionString)
127 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, CLASSNAME, "NpgsqlConnection()");
129 connection_string = NpgsqlConnectionString.ParseConnectionString(ConnectionString);
130 LogConnectionString();
132 NoticeDelegate = new NoticeEventHandler(OnNotice);
133 NotificationDelegate = new NotificationEventHandler(OnNotification);
135 CertificateValidationCallbackDelegate = new CertificateValidationCallback(DefaultCertificateValidationCallback);
136 CertificateSelectionCallbackDelegate = new CertificateSelectionCallback(DefaultCertificateSelectionCallback);
137 PrivateKeySelectionCallbackDelegate = new PrivateKeySelectionCallback(DefaultPrivateKeySelectionCallback);
141 /// Gets or sets the string used to connect to a PostgreSQL database.
142 /// Valid values are:
145 /// Server: Address/Name of Postgresql Server;
148 /// Port: Port to connect to;
151 /// Protocol: Protocol version to use, instead of automatic; Integer 2 or 3;
154 /// Database: Database name. Defaults to user name if not specified;
158 /// User Id: User name;
162 /// Password: Password for clear text authentication;
166 /// SSL: True or False. Controls whether to attempt a secure connection. Default = False;
170 /// Pooling: True or False. Controls whether connection pooling is used. Default = True;
174 /// MinPoolSize: Min size of connection pool;
178 /// MaxPoolSize: Max size of connection pool;
182 /// Encoding: Encoding to be used; Can be ASCII or UNICODE. Default is ASCII. Use UNICODE if you are having problems with accents.
186 /// Timeout: Time to wait for connection open in seconds. Default is 15.
190 /// CommandTimeout: Time to wait for command to finish execution before throw an exception. In seconds. Default is 20.
194 /// Sslmode: Mode for ssl connection control. Can be Prefer, Require, Allow or Disable. Default is Disable. Check user manual for explanation of values.
199 /// ConnectionLifeTime: Time to wait before closing unused connections in the pool in seconds. Default is 15.
203 /// SyncNotification: Specifies if Npgsql should use synchronous notifications.
209 /// <value>The connection string that includes the server name,
210 /// the database name, and other parameters needed to establish
211 /// the initial connection. The default value is an empty string.
215 [RefreshProperties(RefreshProperties.All), DefaultValue(""), RecommendedAsConfigurable(true)]
216 [NpgsqlSysDescription("Description_ConnectionString", typeof(NpgsqlConnection)), Category("Data")]
217 [Editor(typeof(ConnectionStringEditor), typeof(System.Drawing.Design.UITypeEditor))]
220 public String ConnectionString {
223 return connection_string.ToString();
227 // Connection string is used as the key to the connector. Because of this,
228 // we cannot change it while we own a connector.
229 CheckConnectionClosed();
230 NpgsqlEventLog.LogPropertySet(LogLevel.Debug, CLASSNAME, "ConnectionString", value);
231 connection_string = NpgsqlConnectionString.ParseConnectionString(value);
232 LogConnectionString();
237 /// Backend server host name.
243 return connection_string.ToString(ConnectionStringKeys.Host);
248 /// Backend server port.
254 return connection_string.ToInt32(ConnectionStringKeys.Port, ConnectionStringDefaults.Port);
259 /// If true, the connection will attempt to use SSL.
265 return connection_string.ToBool(ConnectionStringKeys.SSL);
270 /// Gets the time to wait while trying to establish a connection
271 /// before terminating the attempt and generating an error.
273 /// <value>The time (in seconds) to wait for a connection to open. The default value is 15 seconds.</value>
276 [NpgsqlSysDescription("Description_ConnectionTimeout", typeof(NpgsqlConnection))]
279 public Int32 ConnectionTimeout {
282 return connection_string.ToInt32(ConnectionStringKeys.Timeout, ConnectionStringDefaults.Timeout);
287 /// Gets the time to wait before closing unused connections in the pool if the count
288 /// of all connections exeeds MinPoolSize.
291 /// If connection pool contains unused connections for ConnectionLifeTime seconds,
292 /// the half of them will be closed. If there will be unused connections in a second
293 /// later then again the half of them will be closed and so on.
294 /// This strategy provide smooth change of connection count in the pool.
296 /// <value>The time (in seconds) to wait. The default value is 15 seconds.</value>
297 public Int32 ConnectionLifeTime {
300 return connection_string.ToInt32(ConnectionStringKeys.ConnectionLifeTime, ConnectionStringDefaults.ConnectionLifeTime);
305 /// Gets the name of the current database or the database to be used after a connection is opened.
307 /// <value>The name of the current database or the name of the database to be
308 /// used after a connection is opened. The default value is the empty string.</value>
310 [NpgsqlSysDescription("Description_Database", typeof(NpgsqlConnection))]
313 public String Database {
316 return connection_string.ToString(ConnectionStringKeys.Database);
321 /// Gets flag indicating if we are using Synchronous notification or not.
322 /// The default value is false.
324 public Boolean SyncNotification
328 return connection_string.ToBool(ConnectionStringKeys.SyncNotification, ConnectionStringDefaults.SyncNotification);
333 /// Gets the current state of the connection.
335 /// <value>A bitwise combination of the <see cref="System.Data.ConnectionState">ConnectionState</see> values. The default is <b>Closed</b>.</value>
337 public ConnectionState State {
342 if (connector != null)
344 return connector.State;
348 return ConnectionState.Closed;
354 /// Version of the PostgreSQL backend.
355 /// This can only be called when there is an active connection.
358 public ServerVersion ServerVersion {
361 CheckConnectionOpen();
362 return connector.ServerVersion;
367 /// Protocol version in use.
368 /// This can only be called when there is an active connection.
371 public ProtocolVersion BackendProtocolVersion {
374 CheckConnectionOpen();
375 return connector.BackendProtocolVersion;
380 /// Begins a database transaction.
382 /// <returns>An <see cref="System.Data.IDbTransaction">IDbTransaction</see>
383 /// object representing the new transaction.</returns>
385 /// Currently there's no support for nested transactions.
387 IDbTransaction IDbConnection.BeginTransaction()
389 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "IDbConnection.BeginTransaction");
391 return BeginTransaction();
395 /// Begins a database transaction with the specified isolation level.
397 /// <param name="level">The <see cref="System.Data.IsolationLevel">isolation level</see> under which the transaction should run.</param>
398 /// <returns>An <see cref="System.Data.IDbTransaction">IDbTransaction</see>
399 /// object representing the new transaction.</returns>
401 /// Currently the IsolationLevel ReadCommitted and Serializable are supported by the PostgreSQL backend.
402 /// There's no support for nested transactions.
404 IDbTransaction IDbConnection.BeginTransaction(IsolationLevel level)
406 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "IDbConnection.BeginTransaction", level);
408 return BeginTransaction(level);
412 /// Begins a database transaction.
414 /// <returns>A <see cref="Npgsql.NpgsqlTransaction">NpgsqlTransaction</see>
415 /// object representing the new transaction.</returns>
417 /// Currently there's no support for nested transactions.
419 public NpgsqlTransaction BeginTransaction()
421 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "BeginTransaction");
422 return this.BeginTransaction(IsolationLevel.ReadCommitted);
426 /// Begins a database transaction with the specified isolation level.
428 /// <param name="level">The <see cref="System.Data.IsolationLevel">isolation level</see> under which the transaction should run.</param>
429 /// <returns>A <see cref="Npgsql.NpgsqlTransaction">NpgsqlTransaction</see>
430 /// object representing the new transaction.</returns>
432 /// Currently the IsolationLevel ReadCommitted and Serializable are supported by the PostgreSQL backend.
433 /// There's no support for nested transactions.
435 public NpgsqlTransaction BeginTransaction(IsolationLevel level)
437 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "BeginTransaction", level);
439 CheckConnectionOpen();
441 if (connector.Transaction != null)
443 throw new InvalidOperationException(resman.GetString("Exception_NoNestedTransactions"));
446 return new NpgsqlTransaction(this, level);
450 /// Opens a database connection with the property settings specified by the
451 /// <see cref="Npgsql.NpgsqlConnection.ConnectionString">ConnectionString</see>.
455 CheckConnectionClosed();
457 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Open");
459 // Check if there is any missing argument.
460 if (! connection_string.Contains(ConnectionStringKeys.Host))
461 throw new ArgumentException(resman.GetString("Exception_MissingConnStrArg"), ConnectionStringKeys.Host);
462 if (! connection_string.Contains(ConnectionStringKeys.UserName))
463 throw new ArgumentException(resman.GetString("Exception_MissingConnStrArg"), ConnectionStringKeys.UserName);
465 // Get a Connector. The connector returned is guaranteed to be connected and ready to go.
466 connector = NpgsqlConnectorPool.ConnectorPoolMgr.RequestConnector (this);
468 connector.Notice += NoticeDelegate;
469 connector.Notification += NotificationDelegate;
471 if (SyncNotification)
472 connector.AddNotificationThread();
477 /// This method changes the current database by disconnecting from the actual
478 /// database and connecting to the specified.
480 /// <param name="dbName">The name of the database to use in place of the current database.</param>
481 public void ChangeDatabase(String dbName)
485 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ChangeDatabase", dbName);
488 throw new ArgumentNullException("dbName");
490 if (dbName == String.Empty)
491 throw new ArgumentOutOfRangeException(String.Format(resman.GetString("Exception_InvalidDbName"), dbName), "dbName");
493 String oldDatabaseName = Database;
497 connection_string[ConnectionStringKeys.Database] = dbName;
503 /// Releases the connection to the database. If the connection is pooled, it will be
504 /// made available for re-use. If it is non-pooled, the actual connection will be shutdown.
510 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Close");
512 if (connector != null)
515 connector.Notification -= NotificationDelegate;
516 connector.Notice -= NoticeDelegate;
518 if (SyncNotification)
519 connector.RemoveNotificationThread();
521 NpgsqlConnectorPool.ConnectorPoolMgr.ReleaseConnector(this, connector);
531 /// Creates and returns a <see cref="System.Data.IDbCommand">IDbCommand</see>
532 /// object associated with the <see cref="System.Data.IDbConnection">IDbConnection</see>.
534 /// <returns>A <see cref="System.Data.IDbCommand">IDbCommand</see> object.</returns>
535 IDbCommand IDbConnection.CreateCommand()
537 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "IDbConnection.CreateCommand");
538 return (NpgsqlCommand) CreateCommand();
542 /// Creates and returns a <see cref="Npgsql.NpgsqlCommand">NpgsqlCommand</see>
543 /// object associated with the <see cref="Npgsql.NpgsqlConnection">NpgsqlConnection</see>.
545 /// <returns>A <see cref="Npgsql.NpgsqlCommand">NpgsqlCommand</see> object.</returns>
546 public NpgsqlCommand CreateCommand()
550 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "CreateCommand");
551 return new NpgsqlCommand("", this);
555 /// Releases all resources used by the
556 /// <see cref="Npgsql.NpgsqlConnection">NpgsqlConnection</see>.
558 /// <param name="disposing"><b>true</b> when called from Dispose();
559 /// <b>false</b> when being called from the finalizer.</param>
560 protected override void Dispose(bool disposing)
567 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Dispose");
572 if (State != ConnectionState.Closed)
574 NpgsqlEventLog.LogMsg(resman, "Log_ConnectionLeaking", LogLevel.Debug);
575 NpgsqlConnectorPool.ConnectorPoolMgr.FixPoolCountBecauseOfConnectionDisposeFalse(this);
579 base.Dispose (disposing);
586 /// Create a new connection based on this one.
588 /// <returns>A new NpgsqlConnection object.</returns>
589 Object ICloneable.Clone()
595 /// Create a new connection based on this one.
597 /// <returns>A new NpgsqlConnection object.</returns>
598 public NpgsqlConnection Clone()
602 NpgsqlConnection C = new NpgsqlConnection(ConnectionString);
604 C.Notice += this.Notice;
606 if (connector != null)
615 // Internal methods and properties
617 internal void OnNotice(object O, NpgsqlNoticeEventArgs E)
625 internal void OnNotification(object O, NpgsqlNotificationEventArgs E)
627 if (Notification != null)
629 Notification(this, E);
634 /// The connector object connected to the backend.
636 internal NpgsqlConnector Connector
645 /// Gets the NpgsqlConnectionString containing the parsed connection string values.
647 internal NpgsqlConnectionString ConnectionStringValues {
650 return connection_string;
657 internal String UserName {
660 return connection_string.ToString(ConnectionStringKeys.UserName);
667 internal String Password {
670 return connection_string.ToString(ConnectionStringKeys.Password);
675 /// Determine if connection pooling will be used for this connection.
677 internal Boolean Pooling {
681 connection_string.ToBool(ConnectionStringKeys.Pooling, ConnectionStringDefaults.Pooling) &&
682 connection_string.ToInt32(ConnectionStringKeys.MaxPoolSize, ConnectionStringDefaults.MaxPoolSize) > 0
687 internal Int32 MinPoolSize {
690 return connection_string.ToInt32(ConnectionStringKeys.MinPoolSize, 0, MaxPoolSize, ConnectionStringDefaults.MinPoolSize);
694 internal Int32 MaxPoolSize {
697 return connection_string.ToInt32(ConnectionStringKeys.MaxPoolSize, 0, 1024, ConnectionStringDefaults.MaxPoolSize);
701 internal Int32 Timeout {
704 return connection_string.ToInt32(ConnectionStringKeys.Timeout, 0, 1024, ConnectionStringDefaults.Timeout);
715 /// Default SSL CertificateSelectionCallback implementation.
717 internal X509Certificate DefaultCertificateSelectionCallback(
718 X509CertificateCollection clientCertificates,
719 X509Certificate serverCertificate,
721 X509CertificateCollection serverRequestedCertificates)
723 if (CertificateSelectionCallback != null)
725 return CertificateSelectionCallback(clientCertificates, serverCertificate, targetHost, serverRequestedCertificates);
734 /// Default SSL CertificateValidationCallback implementation.
736 internal bool DefaultCertificateValidationCallback(
737 X509Certificate certificate,
738 int[] certificateErrors)
740 if (CertificateValidationCallback != null)
742 return CertificateValidationCallback(certificate, certificateErrors);
751 /// Default SSL PrivateKeySelectionCallback implementation.
753 internal AsymmetricAlgorithm DefaultPrivateKeySelectionCallback(
754 X509Certificate certificate,
757 if (PrivateKeySelectionCallback != null)
759 return PrivateKeySelectionCallback(certificate, targetHost);
770 // Private methods and properties
775 /// Write each key/value pair in the connection string to the log.
777 private void LogConnectionString()
779 foreach (DictionaryEntry DE in connection_string)
781 NpgsqlEventLog.LogMsg(resman, "Log_ConnectionStringValues", LogLevel.Debug, DE.Key, DE.Value);
785 private void CheckConnectionOpen()
789 throw new ObjectDisposedException(CLASSNAME);
792 if (connector == null)
794 throw new InvalidOperationException(resman.GetString("Exception_ConnNotOpen"));
798 private void CheckConnectionClosed()
802 throw new ObjectDisposedException(CLASSNAME);
805 if (connector != null)
807 throw new InvalidOperationException(resman.GetString("Exception_ConnOpen"));
811 private void CheckNotDisposed()
815 throw new ObjectDisposedException(CLASSNAME);
821 /// Returns the supported collections
823 public DataTable GetSchema()
825 return NpgsqlSchema.GetMetaDataCollections();
829 /// Returns the schema collection specified by the collection name.
831 /// <param name="collectionName">The collection name.</param>
832 /// <returns>The collection specified.</returns>
833 public DataTable GetSchema(string collectionName)
835 return GetSchema(collectionName, null);
839 /// Returns the schema collection specified by the collection name filtered by the restrictions.
841 /// <param name="collectionName">The collection name.</param>
842 /// <param name="restrictions">
843 /// The restriction values to filter the results. A description of the restrictions is contained
844 /// in the Restrictions collection.
846 /// <returns>The collection specified.</returns>
847 public DataTable GetSchema(string collectionName, string[] restrictions)
849 switch(collectionName)
851 case "MetaDataCollections":
852 return NpgsqlSchema.GetMetaDataCollections();
854 return NpgsqlSchema.GetRestrictions();
855 case "DataSourceInformation":
857 case "ReservedWords":
858 throw new NotSupportedException();
859 // custom collections for npgsql
861 return new NpgsqlSchema(new NpgsqlConnection(ConnectionString)).GetDatabases(restrictions);
863 return new NpgsqlSchema(new NpgsqlConnection(ConnectionString)).GetTables(restrictions);
865 return new NpgsqlSchema(new NpgsqlConnection(ConnectionString)).GetColumns(restrictions);
867 return new NpgsqlSchema(new NpgsqlConnection(ConnectionString)).GetViews(restrictions);
869 return new NpgsqlSchema(new NpgsqlConnection(ConnectionString)).GetUsers(restrictions);
871 throw new NotSupportedException();
875 public void ClearPool()
877 NpgsqlConnectorPool.ConnectorPoolMgr.ClearPool(this);
880 public void ClearAllPools()
882 NpgsqlConnectorPool.ConnectorPoolMgr.ClearAllPools();