// created on 10/5/2002 at 23:01
// Npgsql.NpgsqlConnection.cs
//
// Author:
// Francisco Jr. (fxjrlists@yahoo.com.br)
//
// Copyright (C) 2002 The Npgsql Development Team
// npgsql-general@gborg.postgresql.org
// http://gborg.postgresql.org/project/npgsql/projdisplay.php
//
//
// Permission to use, copy, modify, and distribute this software and its
// documentation for any purpose, without fee, and without a written
// agreement is hereby granted, provided that the above copyright notice
// and this paragraph and the following two paragraphs appear in all copies.
//
// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY
// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF
// THE POSSIBILITY OF SUCH DAMAGE.
//
// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS
// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
using System;
using System.ComponentModel;
using System.Data;
using System.Data.Common;
using System.Net.Security;
using System.Reflection;
using System.Resources;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Transactions;
using Common.Logging;
using Mono.Security.Protocol.Tls;
using Npgsql.Localization;
using IsolationLevel = System.Data.IsolationLevel;
namespace Npgsql
{
///
/// This class represents a connection to a PostgreSQL server.
///
#if WITHDESIGN
[System.Drawing.ToolboxBitmapAttribute(typeof(NpgsqlConnection))]
#endif
[System.ComponentModel.DesignerCategory("")]
public sealed class NpgsqlConnection : DbConnection, ICloneable
{
#region Fields
// Parsed connection string cache
static readonly Cache Cache = new Cache();
// Set this when disposed is called.
bool _disposed;
// Used when we closed the connector due to an error, but are pretending it's open.
bool _fakingOpen;
// Used when the connection is closed but an TransactionScope is still active
// (the actual close is postponed until the scope ends)
bool _postponingClose;
bool _postponingDispose;
// Strong-typed ConnectionString values
NpgsqlConnectionStringBuilder _settings;
///
/// The connector object connected to the backend.
///
internal NpgsqlConnector Connector { get; private set; }
NpgsqlPromotableSinglePhaseNotification _promotable;
// A cached copy of the result of `settings.ConnectionString`
string _connectionString;
static readonly ILog _log = LogManager.GetCurrentClassLogger();
#endregion Fields
#region Constructors / Init / Open
///
/// Initializes a new instance of the
/// NpgsqlConnection class.
///
public NpgsqlConnection() : this(String.Empty) {}
///
/// Initializes a new instance of the
/// NpgsqlConnection class
/// and sets the ConnectionString.
///
/// The connection used to open the PostgreSQL database.
public NpgsqlConnection(string connectionString)
{
LoadConnectionStringBuilder(connectionString);
Init();
}
///
/// Initializes a new instance of the
/// NpgsqlConnection class
/// and sets the ConnectionString.
///
/// The connection used to open the PostgreSQL database.
public NpgsqlConnection(NpgsqlConnectionStringBuilder connectionString)
{
LoadConnectionStringBuilder(connectionString);
Init();
}
void Init()
{
NoticeDelegate = OnNotice;
NotificationDelegate = OnNotification;
ProvideClientCertificatesCallbackDelegate = DefaultProvideClientCertificatesCallback;
CertificateValidationCallbackDelegate = DefaultCertificateValidationCallback;
CertificateSelectionCallbackDelegate = DefaultCertificateSelectionCallback;
PrivateKeySelectionCallbackDelegate = DefaultPrivateKeySelectionCallback;
ValidateRemoteCertificateCallbackDelegate = DefaultValidateRemoteCertificateCallback;
// Fix authentication problems. See https://bugzilla.novell.com/show_bug.cgi?id=MONO77559 and
// http://pgfoundry.org/forum/message.php?msg_id=1002377 for more info.
RSACryptoServiceProvider.UseMachineKeyStore = true;
_promotable = new NpgsqlPromotableSinglePhaseNotification(this);
}
///
/// Opens a database connection with the property settings specified by the
/// ConnectionString.
///
public override void Open()
{
// If we're postponing a close (see doc on this variable), the connection is already
// open and can be silently reused
if (_postponingClose)
return;
CheckConnectionClosed();
_log.Debug("Opening connnection");
// Check if there is any missing argument.
if (!_settings.ContainsKey(Keywords.Host))
{
throw new ArgumentException(L10N.MissingConnStrArg, Keywords.Host.ToString());
}
if (!_settings.ContainsKey(Keywords.UserName) && !_settings.ContainsKey(Keywords.IntegratedSecurity))
{
throw new ArgumentException(L10N.MissingConnStrArg, Keywords.UserName.ToString());
}
// Get a Connector, either from the pool or creating one ourselves.
if (Pooling)
{
Connector = NpgsqlConnectorPool.ConnectorPoolMgr.RequestConnector(this);
}
else
{
Connector = new NpgsqlConnector(this);
Connector.ProvideClientCertificatesCallback += ProvideClientCertificatesCallbackDelegate;
Connector.CertificateSelectionCallback += CertificateSelectionCallbackDelegate;
Connector.CertificateValidationCallback += CertificateValidationCallbackDelegate;
Connector.PrivateKeySelectionCallback += PrivateKeySelectionCallbackDelegate;
Connector.ValidateRemoteCertificateCallback += ValidateRemoteCertificateCallbackDelegate;
Connector.Open();
}
Connector.Notice += NoticeDelegate;
Connector.Notification += NotificationDelegate;
if (SyncNotification)
{
Connector.AddNotificationThread();
}
if (Enlist)
{
Promotable.Enlist(Transaction.Current);
}
OnStateChange(new StateChangeEventArgs(ConnectionState.Closed, ConnectionState.Open));
}
#endregion Open / Init
#region Connection string management
///
/// Gets or sets the string used to connect to a PostgreSQL database. See the manual for details.
///
/// The connection string that includes the server name,
/// the database name, and other parameters needed to establish
/// the initial connection. The default value is an empty string.
///
#if WITHDESIGN
[RefreshProperties(RefreshProperties.All), DefaultValue(""), RecommendedAsConfigurable(true)]
[NpgsqlSysDescription("Description_ConnectionString", typeof(NpgsqlConnection)), Category("Data")]
[Editor(typeof(ConnectionStringEditor), typeof(System.Drawing.Design.UITypeEditor))]
#endif
public override String ConnectionString
{
get
{
if (string.IsNullOrEmpty(_connectionString))
RefreshConnectionString();
return _settings.ConnectionString;
}
set
{
// Connection string is used as the key to the connector. Because of this,
// we cannot change it while we own a connector.
CheckConnectionClosed();
var builder = Cache[value];
_settings = builder == null ? new NpgsqlConnectionStringBuilder(value) : builder.Clone();
LoadConnectionStringBuilder(value);
}
}
///
/// Returns a copy of the NpgsqlConnectionStringBuilder that contains the parsed connection string values.
///
internal NpgsqlConnectionStringBuilder CopyConnectionStringBuilder()
{
return _settings.Clone();
}
///
/// Sets the `settings` ConnectionStringBuilder based on the given `connectionString`
///
/// The connection string to load the builder from
void LoadConnectionStringBuilder(string connectionString)
{
var newSettings = Cache[connectionString];
if (newSettings == null)
{
newSettings = new NpgsqlConnectionStringBuilder(connectionString);
Cache[connectionString] = newSettings;
}
LoadConnectionStringBuilder(newSettings);
}
///
/// Sets the `settings` ConnectionStringBuilder based on the given `connectionString`
///
/// The connection string to load the builder from
void LoadConnectionStringBuilder(NpgsqlConnectionStringBuilder connectionString)
{
// Clone the settings, because if Integrated Security is enabled, user ID can be different
_settings = connectionString.Clone();
// Set the UserName explicitly to freeze any Integrated Security-determined names
if (_settings.IntegratedSecurity)
_settings.UserName = _settings.UserName;
RefreshConnectionString();
if (_log.IsTraceEnabled)
{
foreach (string key in _settings.Keys)
{
_log.TraceFormat("Connstring dump {0}={1}", key, _settings[key]);
}
}
}
///
/// Refresh the cached _connectionString whenever the builder settings change
///
void RefreshConnectionString()
{
_connectionString = _settings.ConnectionString;
}
#endregion Connection string management
#region Configuration settings
///
/// Backend server host name.
///
[Browsable(true)]
public string Host
{
get { return _settings.Host; }
}
///
/// Backend server port.
///
[Browsable(true)]
public int Port
{
get { return _settings.Port; }
}
///
/// If true, the connection will attempt to use SSL.
///
[Browsable(true)]
public bool SSL
{
get { return _settings.SSL; }
}
///
/// If true, the connection will attempt to use SslStream instead of Mono.Security.
///
public bool UseSslStream
{
get { return NpgsqlConnector.UseSslStream; }
set { NpgsqlConnector.UseSslStream = value; }
}
///
/// Gets the time to wait while trying to establish a connection
/// before terminating the attempt and generating an error.
///
/// The time (in seconds) to wait for a connection to open. The default value is 15 seconds.
#if WITHDESIGN
[NpgsqlSysDescription("Description_ConnectionTimeout", typeof(NpgsqlConnection))]
#endif
public override int ConnectionTimeout
{
get { return _settings.Timeout; }
}
///
/// Gets the time to wait while trying to execute a command
/// before terminating the attempt and generating an error.
///
/// The time (in seconds) to wait for a command to complete. The default value is 20 seconds.
public int CommandTimeout
{
get { return _settings.CommandTimeout; }
}
///
/// Gets the time to wait before closing unused connections in the pool if the count
/// of all connections exeeds MinPoolSize.
///
///
/// If connection pool contains unused connections for ConnectionLifeTime seconds,
/// the half of them will be closed. If there will be unused connections in a second
/// later then again the half of them will be closed and so on.
/// This strategy provide smooth change of connection count in the pool.
///
/// The time (in seconds) to wait. The default value is 15 seconds.
public int ConnectionLifeTime
{
get { return _settings.ConnectionLifeTime; }
}
///
/// Gets the name of the current database or the database to be used after a connection is opened.
///
/// The name of the current database or the name of the database to be
/// used after a connection is opened. The default value is the empty string.
#if WITHDESIGN
[NpgsqlSysDescription("Description_Database", typeof(NpgsqlConnection))]
#endif
public override string Database
{
get { return _settings.Database; }
}
///
/// Gets the database server name.
///
public override string DataSource
{
get { return _settings.Host; }
}
///
/// Gets flag indicating if we are using Synchronous notification or not.
/// The default value is false.
///
public bool SyncNotification
{
get { return _settings.SyncNotification; }
}
///
/// Compatibility version.
///
public Version NpgsqlCompatibilityVersion
{
get { return _settings.Compatible; }
}
///
/// User name.
///
internal string UserName
{
get { return _settings.UserName; }
}
///
/// Use extended types.
///
public bool UseExtendedTypes
{
get
{
bool ext = _settings.UseExtendedTypes;
return ext;
}
}
///
/// Password.
///
internal byte[] Password
{
get { return _settings.PasswordAsByteArray; }
}
///
/// Determine if connection pooling will be used for this connection.
///
internal bool Pooling
{
get { return (_settings.Pooling && (_settings.MaxPoolSize > 0)); }
}
internal int MinPoolSize
{
get { return _settings.MinPoolSize; }
}
internal int MaxPoolSize
{
get { return _settings.MaxPoolSize; }
}
internal int Timeout
{
get { return _settings.Timeout; }
}
internal bool Enlist
{
get { return _settings.Enlist; }
}
#endregion Configuration settings
#region State management
///
/// Gets the current state of the connection.
///
/// A bitwise combination of the ConnectionState values. The default is Closed.
[Browsable(false)]
public ConnectionState FullState
{
get
{
if (Connector == null || _disposed)
{
return ConnectionState.Closed;
}
switch (Connector.State)
{
case ConnectorState.Closed:
return ConnectionState.Closed;
case ConnectorState.Connecting:
return ConnectionState.Connecting;
case ConnectorState.Ready:
return ConnectionState.Open;
case ConnectorState.Executing:
return ConnectionState.Open | ConnectionState.Executing;
case ConnectorState.Fetching:
return ConnectionState.Open | ConnectionState.Fetching;
case ConnectorState.Broken:
return ConnectionState.Broken;
case ConnectorState.CopyIn:
return ConnectionState.Open | ConnectionState.Fetching;
case ConnectorState.CopyOut:
return ConnectionState.Closed | ConnectionState.Fetching;
default:
throw new ArgumentOutOfRangeException("Unknown connector state: " + Connector.State);
}
}
}
///
/// Gets whether the current state of the connection is Open or Closed
///
/// ConnectionState.Open, ConnectionState.Closed or ConnectionState.Connecting
[Browsable(false)]
public override ConnectionState State
{
get
{
var s = FullState;
if ((s & ConnectionState.Open) != 0)
return ConnectionState.Open;
if ((s & ConnectionState.Connecting) != 0)
return ConnectionState.Connecting;
return ConnectionState.Closed;
}
}
#endregion State management
#region Commands
///
/// Creates and returns a DbCommand
/// object associated with the IDbConnection.
///
/// A DbCommand object.
protected override DbCommand CreateDbCommand()
{
return CreateCommand();
}
///
/// Creates and returns a NpgsqlCommand
/// object associated with the NpgsqlConnection.
///
/// A NpgsqlCommand object.
public new NpgsqlCommand CreateCommand()
{
CheckNotDisposed();
return new NpgsqlCommand("", this);
}
#endregion Commands
#region Transactions
///
/// Begins a database transaction with the specified isolation level.
///
/// The isolation level under which the transaction should run.
/// An DbTransaction
/// object representing the new transaction.
///
/// Currently the IsolationLevel ReadCommitted and Serializable are supported by the PostgreSQL backend.
/// There's no support for nested transactions.
///
protected override DbTransaction BeginDbTransaction(IsolationLevel isolationLevel)
{
return BeginTransaction(isolationLevel);
}
///
/// Begins a database transaction.
///
/// A NpgsqlTransaction
/// object representing the new transaction.
///
/// Currently there's no support for nested transactions.
///
public new NpgsqlTransaction BeginTransaction()
{
return BeginTransaction(IsolationLevel.ReadCommitted);
}
///
/// Begins a database transaction with the specified isolation level.
///
/// The isolation level under which the transaction should run.
/// A NpgsqlTransaction
/// object representing the new transaction.
///
/// Currently the IsolationLevel ReadCommitted and Serializable are supported by the PostgreSQL backend.
/// There's no support for nested transactions.
///
public new NpgsqlTransaction BeginTransaction(IsolationLevel level)
{
_log.Debug("Beginning transaction with isolation level " + level);
CheckConnectionOpen();
if (Connector.Transaction != null) {
throw new InvalidOperationException(L10N.NoNestedTransactions);
}
return new NpgsqlTransaction(this, level);
}
///
/// When a connection is closed within an enclosing TransactionScope and the transaction
/// hasn't been promoted, we defer the actual closing until the scope ends.
///
internal void PromotableLocalTransactionEnded()
{
if (_postponingDispose)
Dispose(true);
else if (_postponingClose)
ReallyClose();
}
///
/// Enlist transation.
///
///
public override void EnlistTransaction(Transaction transaction)
{
Promotable.Enlist(transaction);
}
#endregion
#region Close
internal void EmergencyClose()
{
_fakingOpen = true;
}
///
/// Releases the connection to the database. If the connection is pooled, it will be
/// made available for re-use. If it is non-pooled, the actual connection will be shutdown.
///
public override void Close()
{
if (Connector == null)
return;
_log.Debug("Closing connection");
if (_promotable != null && _promotable.InLocalTransaction)
{
_postponingClose = true;
return;
}
ReallyClose();
}
void ReallyClose()
{
_log.Trace("Really closing connection");
_postponingClose = false;
// clear the way for another promotable transaction
_promotable = null;
Connector.Notification -= NotificationDelegate;
Connector.Notice -= NoticeDelegate;
if (SyncNotification)
{
Connector.RemoveNotificationThread();
}
if (Pooling)
{
NpgsqlConnectorPool.ConnectorPoolMgr.ReleaseConnector(this, Connector);
}
else
{
Connector.ProvideClientCertificatesCallback -= ProvideClientCertificatesCallbackDelegate;
Connector.CertificateSelectionCallback -= CertificateSelectionCallbackDelegate;
Connector.CertificateValidationCallback -= CertificateValidationCallbackDelegate;
Connector.PrivateKeySelectionCallback -= PrivateKeySelectionCallbackDelegate;
Connector.ValidateRemoteCertificateCallback -= ValidateRemoteCertificateCallbackDelegate;
if (Connector.Transaction != null)
{
Connector.Transaction.Cancel();
}
Connector.Close();
}
Connector = null;
OnStateChange(new StateChangeEventArgs(ConnectionState.Open, ConnectionState.Closed));
}
///
/// Releases all resources used by the
/// NpgsqlConnection.
///
/// true when called from Dispose();
/// false when being called from the finalizer.
protected override void Dispose(bool disposing)
{
if (_disposed)
return;
_postponingDispose = false;
if (disposing)
{
Close();
if (_postponingClose)
{
_postponingDispose = true;
return;
}
}
base.Dispose(disposing);
_disposed = true;
}
#endregion
#region Notifications
///
/// Occurs on NoticeResponses from the PostgreSQL backend.
///
public event NoticeEventHandler Notice;
internal NoticeEventHandler NoticeDelegate;
///
/// Occurs on NotificationResponses from the PostgreSQL backend.
///
public event NotificationEventHandler Notification;
internal NotificationEventHandler NotificationDelegate;
//
// Internal methods and properties
//
internal void OnNotice(object o, NpgsqlNoticeEventArgs e)
{
if (Notice != null)
{
Notice(this, e);
}
}
internal void OnNotification(object o, NpgsqlNotificationEventArgs e)
{
if (Notification != null)
{
Notification(this, e);
}
}
#endregion Notifications
#region SSL
///
/// Returns whether SSL is being used for the connection.
///
internal bool IsSecure
{
get
{
CheckConnectionOpen();
return Connector.IsSecure;
}
}
///
/// Called to provide client certificates for SSL handshake.
///
public event ProvideClientCertificatesCallback ProvideClientCertificatesCallback;
internal ProvideClientCertificatesCallback ProvideClientCertificatesCallbackDelegate;
///
/// Mono.Security.Protocol.Tls.CertificateSelectionCallback delegate.
///
[Obsolete("CertificateSelectionCallback, CertificateValidationCallback and PrivateKeySelectionCallback have been replaced with ValidateRemoteCertificateCallback.")]
public event CertificateSelectionCallback CertificateSelectionCallback;
internal CertificateSelectionCallback CertificateSelectionCallbackDelegate;
///
/// Mono.Security.Protocol.Tls.CertificateValidationCallback delegate.
///
[Obsolete("CertificateSelectionCallback, CertificateValidationCallback and PrivateKeySelectionCallback have been replaced with ValidateRemoteCertificateCallback.")]
public event CertificateValidationCallback CertificateValidationCallback;
internal CertificateValidationCallback CertificateValidationCallbackDelegate;
///
/// Mono.Security.Protocol.Tls.PrivateKeySelectionCallback delegate.
///
[Obsolete("CertificateSelectionCallback, CertificateValidationCallback and PrivateKeySelectionCallback have been replaced with ValidateRemoteCertificateCallback.")]
public event PrivateKeySelectionCallback PrivateKeySelectionCallback;
internal PrivateKeySelectionCallback PrivateKeySelectionCallbackDelegate;
///
/// Called to validate server's certificate during SSL handshake
///
public event ValidateRemoteCertificateCallback ValidateRemoteCertificateCallback;
internal ValidateRemoteCertificateCallback ValidateRemoteCertificateCallbackDelegate;
///
/// Default SSL CertificateSelectionCallback implementation.
///
internal X509Certificate DefaultCertificateSelectionCallback(X509CertificateCollection clientCertificates,
X509Certificate serverCertificate, string targetHost,
X509CertificateCollection serverRequestedCertificates)
{
return CertificateSelectionCallback != null
? CertificateSelectionCallback(clientCertificates, serverCertificate, targetHost, serverRequestedCertificates)
: null;
}
///
/// Default SSL CertificateValidationCallback implementation.
///
internal bool DefaultCertificateValidationCallback(X509Certificate certificate, int[] certificateErrors)
{
return CertificateValidationCallback == null || CertificateValidationCallback(certificate, certificateErrors);
}
///
/// Default SSL PrivateKeySelectionCallback implementation.
///
internal AsymmetricAlgorithm DefaultPrivateKeySelectionCallback(X509Certificate certificate, string targetHost)
{
return PrivateKeySelectionCallback != null
? PrivateKeySelectionCallback(certificate, targetHost)
: null;
}
///
/// Default SSL ProvideClientCertificatesCallback implementation.
///
internal void DefaultProvideClientCertificatesCallback(X509CertificateCollection certificates)
{
if (ProvideClientCertificatesCallback != null) {
ProvideClientCertificatesCallback(certificates);
}
}
///
/// Default SSL ValidateRemoteCertificateCallback implementation.
///
internal bool DefaultValidateRemoteCertificateCallback(X509Certificate cert, X509Chain chain, SslPolicyErrors errors)
{
return ValidateRemoteCertificateCallback == null || ValidateRemoteCertificateCallback(cert, chain, errors);
}
#endregion SSL
#region Backend version and capabilities
///
/// Version of the PostgreSQL backend.
/// This can only be called when there is an active connection.
///
[Browsable(false)]
public Version PostgreSqlVersion
{
get
{
CheckConnectionOpen();
return Connector.ServerVersion;
}
}
///
/// PostgreSQL server version.
///
public override string ServerVersion
{
get { return PostgreSqlVersion.ToString(); }
}
///
/// Protocol version in use.
/// This can only be called when there is an active connection.
/// Always retuna Version3
///
[Browsable(false)]
public ProtocolVersion BackendProtocolVersion
{
get
{
CheckConnectionOpen();
return ProtocolVersion.Version3;
}
}
///
/// Process id of backend server.
/// This can only be called when there is an active connection.
///
[Browsable(false)]
public int ProcessID
{
get
{
CheckConnectionOpen();
return Connector.BackEndKeyData.ProcessID;
}
}
///
/// Report whether the backend is expecting standard conformant strings.
/// In version 8.1, Postgres began reporting this value (false), but did not actually support standard conformant strings.
/// In version 8.2, Postgres began supporting standard conformant strings, but defaulted this flag to false.
/// As of version 9.1, this flag defaults to true.
///
[Browsable(false)]
public bool UseConformantStrings
{
get
{
CheckConnectionOpen();
return Connector.NativeToBackendTypeConverterOptions.UseConformantStrings;
}
}
///
/// Report whether the backend understands the string literal E prefix (>= 8.1).
///
[Browsable(false)]
public bool Supports_E_StringPrefix
{
get
{
CheckConnectionOpen();
return Connector.NativeToBackendTypeConverterOptions.Supports_E_StringPrefix;
}
}
///
/// Report whether the backend understands the hex byte format (>= 9.0).
///
[Browsable(false)]
public bool SupportsHexByteFormat
{
get
{
CheckConnectionOpen();
return Connector.NativeToBackendTypeConverterOptions.SupportsHexByteFormat;
}
}
#endregion Backend version and capabilities
#region State checks
NpgsqlPromotableSinglePhaseNotification Promotable
{
get { return _promotable ?? (_promotable = new NpgsqlPromotableSinglePhaseNotification(this)); }
}
void CheckConnectionOpen()
{
if (_disposed) {
throw new ObjectDisposedException(typeof(NpgsqlConnection).Name);
}
if (_fakingOpen)
{
if (Connector != null)
{
try
{
Close();
}
catch
{
}
}
Open();
_fakingOpen = false;
}
if (_postponingClose || Connector == null)
{
throw new InvalidOperationException(L10N.ConnNotOpen);
}
}
void CheckConnectionClosed()
{
if (_disposed) {
throw new ObjectDisposedException(typeof(NpgsqlConnection).Name);
}
if (Connector != null) {
throw new InvalidOperationException(L10N.ConnNotOpen);
}
}
void CheckNotDisposed()
{
if (_disposed) {
throw new ObjectDisposedException(typeof(NpgsqlConnection).Name);
}
}
#endregion State checks
#region Schema operations
///
/// Returns the supported collections
///
public override DataTable GetSchema()
{
return NpgsqlSchema.GetMetaDataCollections();
}
///
/// Returns the schema collection specified by the collection name.
///
/// The collection name.
/// The collection specified.
public override DataTable GetSchema(string collectionName)
{
return GetSchema(collectionName, null);
}
///
/// Returns the schema collection specified by the collection name filtered by the restrictions.
///
/// The collection name.
///
/// The restriction values to filter the results. A description of the restrictions is contained
/// in the Restrictions collection.
///
/// The collection specified.
public override DataTable GetSchema(string collectionName, string[] restrictions)
{
using (var tempConn = new NpgsqlConnection(ConnectionString))
{
switch (collectionName)
{
case "MetaDataCollections":
return NpgsqlSchema.GetMetaDataCollections();
case "Restrictions":
return NpgsqlSchema.GetRestrictions();
case "DataSourceInformation":
return NpgsqlSchema.GetDataSourceInformation();
case "DataTypes":
throw new NotSupportedException();
case "ReservedWords":
return NpgsqlSchema.GetReservedWords();
// custom collections for npgsql
case "Databases":
return NpgsqlSchema.GetDatabases(tempConn, restrictions);
case "Schemata":
return NpgsqlSchema.GetSchemata(tempConn, restrictions);
case "Tables":
return NpgsqlSchema.GetTables(tempConn, restrictions);
case "Columns":
return NpgsqlSchema.GetColumns(tempConn, restrictions);
case "Views":
return NpgsqlSchema.GetViews(tempConn, restrictions);
case "Users":
return NpgsqlSchema.GetUsers(tempConn, restrictions);
case "Indexes":
return NpgsqlSchema.GetIndexes(tempConn, restrictions);
case "IndexColumns":
return NpgsqlSchema.GetIndexColumns(tempConn, restrictions);
case "Constraints":
case "PrimaryKey":
case "UniqueKeys":
case "ForeignKeys":
return NpgsqlSchema.GetConstraints(tempConn, restrictions, collectionName);
case "ConstraintColumns":
return NpgsqlSchema.GetConstraintColumns(tempConn, restrictions);
default:
throw new ArgumentOutOfRangeException("collectionName", collectionName, "Invalid collection name");
}
}
}
#endregion Schema operations
#region Misc
///
/// This method changes the current database by disconnecting from the actual
/// database and connecting to the specified.
///
/// The name of the database to use in place of the current database.
public override void ChangeDatabase(String dbName)
{
_log.Debug("Changing database to " + dbName);
CheckNotDisposed();
if (dbName == null)
{
throw new ArgumentNullException("dbName");
}
if (string.IsNullOrEmpty(dbName))
{
throw new ArgumentOutOfRangeException("dbName", dbName, String.Format(L10N.InvalidDbName));
}
Close();
// Mutating the current `settings` object would invalidate the cached instance, so work on a copy instead.
_settings = _settings.Clone();
_settings[Keywords.Database] = dbName;
_connectionString = null;
Open();
}
///
/// Create a new connection based on this one.
///
/// A new NpgsqlConnection object.
Object ICloneable.Clone()
{
return Clone();
}
///
/// Create a new connection based on this one.
///
/// A new NpgsqlConnection object.
public NpgsqlConnection Clone()
{
CheckNotDisposed();
var clone = new NpgsqlConnection(ConnectionString);
clone.Notice += Notice;
if (Connector != null)
{
clone.Open();
}
return clone;
}
///
/// DB provider factory.
///
protected override DbProviderFactory DbProviderFactory
{
get { return NpgsqlFactory.Instance; }
}
///
/// Clear connection pool.
///
public void ClearPool()
{
NpgsqlConnectorPool.ConnectorPoolMgr.ClearPool(this);
}
///
/// Clear all connection pools.
///
public static void ClearAllPools()
{
NpgsqlConnectorPool.ConnectorPoolMgr.ClearAllPools();
}
#endregion Misc
}
///
/// Represents the method that handles the Notice events.
///
/// The source of the event.
/// A NpgsqlNoticeEventArgs that contains the event data.
public delegate void NoticeEventHandler(Object sender, NpgsqlNoticeEventArgs e);
///
/// Represents the method that handles the Notification events.
///
/// The source of the event.
/// A NpgsqlNotificationEventArgs that contains the event data.
public delegate void NotificationEventHandler(Object sender, NpgsqlNotificationEventArgs e);
}