X Tutup
using System; using System.IO; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Data.Common; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using Npgsql.Internal; using Npgsql.Replication; namespace Npgsql; /// /// Provides a simple way to create and manage the contents of connection strings used by /// the class. /// [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2112:ReflectionToRequiresUnreferencedCode", Justification = "Suppressing the same warnings as suppressed in the base DbConnectionStringBuilder. See https://github.com/dotnet/runtime/issues/97057")] [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2113:ReflectionToRequiresUnreferencedCode", Justification = "Suppressing the same warnings as suppressed in the base DbConnectionStringBuilder. See https://github.com/dotnet/runtime/issues/97057")] public sealed partial class NpgsqlConnectionStringBuilder : DbConnectionStringBuilder, IDictionary { #region Fields /// /// Cached DataSource value to reduce allocations on NpgsqlConnection.DataSource.get /// string? _dataSourceCached; internal string? DataSourceCached => _dataSourceCached ??= _host is null || _host.Contains(",") ? null : IsUnixSocket(_host, _port, out var socketPath, replaceForAbstract: false) ? socketPath : $"tcp://{_host}:{_port}"; // Note that we can't cache the result due to nullable's assignment not being thread safe internal TimeSpan HostRecheckSecondsTranslated => TimeSpan.FromSeconds(HostRecheckSeconds == 0 ? -1 : HostRecheckSeconds); #endregion #region Constructors /// /// Initializes a new instance of the NpgsqlConnectionStringBuilder class. /// public NpgsqlConnectionStringBuilder() => Init(); /// /// Initializes a new instance of the NpgsqlConnectionStringBuilder class, optionally using ODBC rules for quoting values. /// /// true to use {} to delimit fields; false to use quotation marks. public NpgsqlConnectionStringBuilder(bool useOdbcRules) : base(useOdbcRules) => Init(); /// /// Initializes a new instance of the NpgsqlConnectionStringBuilder class and sets its . /// public NpgsqlConnectionStringBuilder(string? connectionString) { Init(); ConnectionString = connectionString; } // Method fake-returns an int only to make sure it's code-generated private partial int Init(); /// /// GeneratedAction and GeneratedActions exist to be able to produce a streamlined binary footprint for NativeAOT. /// An idiomatic approach where each action has its own method would double the binary size of NpgsqlConnectionStringBuilder. /// enum GeneratedAction { Set, Get, Remove, GetCanonical } private partial bool GeneratedActions(GeneratedAction action, string keyword, ref object? value); #endregion #region Non-static property handling /// /// Gets or sets the value associated with the specified key. /// /// The key of the item to get or set. /// The value associated with the specified key. [AllowNull] public override object this[string keyword] { get { if (!TryGetValue(keyword, out var value)) throw new ArgumentException("Keyword not supported: " + keyword, nameof(keyword)); return value; } set { if (value is null) { Remove(keyword); return; } try { var val = value; GeneratedActions(GeneratedAction.Set, keyword.ToUpperInvariant(), ref val); } catch (Exception e) { throw new ArgumentException("Couldn't set " + keyword, keyword, e); } } } object? IDictionary.this[string keyword] { get => this[keyword]; set => this[keyword] = value!; } /// /// Adds an item to the . /// /// The key-value pair to be added. public void Add(KeyValuePair item) => this[item.Key] = item.Value!; void IDictionary.Add(string keyword, object? value) => this[keyword] = value; /// /// Removes the entry with the specified key from the DbConnectionStringBuilder instance. /// /// The key of the key/value pair to be removed from the connection string in this DbConnectionStringBuilder. /// true if the key existed within the connection string and was removed; false if the key did not exist. public override bool Remove(string keyword) { object? value = null; return GeneratedActions(GeneratedAction.Remove, keyword.ToUpperInvariant(), ref value); } /// /// Removes the entry from the DbConnectionStringBuilder instance. /// /// The key/value pair to be removed from the connection string in this DbConnectionStringBuilder. /// true if the key existed within the connection string and was removed; false if the key did not exist. public bool Remove(KeyValuePair item) => Remove(item.Key); /// /// Clears the contents of the instance. /// public override void Clear() { Debug.Assert(Keys != null); foreach (var k in (string[])Keys) Remove(k); } /// /// Determines whether the contains a specific key. /// /// The key to locate in the . /// true if the contains an entry with the specified key; otherwise false. public override bool ContainsKey(string keyword) { object? value = null; return GeneratedActions(GeneratedAction.GetCanonical, (keyword ?? throw new ArgumentNullException(nameof(keyword))).ToUpperInvariant(), ref value); } /// /// Determines whether the contains a specific key-value pair. /// /// The item to locate in the . /// true if the contains the entry; otherwise false. public bool Contains(KeyValuePair item) => TryGetValue(item.Key, out var value) && ((value == null && item.Value == null) || (value != null && value.Equals(item.Value))); /// /// Retrieves a value corresponding to the supplied key from this . /// /// The key of the item to retrieve. /// The value corresponding to the key. /// true if keyword was found within the connection string, false otherwise. public override bool TryGetValue(string keyword, [NotNullWhen(true)] out object? value) { object? v = null; var result = GeneratedActions(GeneratedAction.Get, (keyword ?? throw new ArgumentNullException(nameof(keyword))).ToUpperInvariant(), ref v); value = v; return result; } void SetValue(string propertyName, object? value) { object? canonicalKeyword = null; var result = GeneratedActions(GeneratedAction.GetCanonical, (propertyName ?? throw new ArgumentNullException(nameof(propertyName))).ToUpperInvariant(), ref canonicalKeyword); if (!result) throw new KeyNotFoundException(); if (value == null) base.Remove((string)canonicalKeyword!); else base[(string)canonicalKeyword!] = value; } #endregion #region Properties - Connection /// /// The hostname or IP address of the PostgreSQL server to connect to. /// [Category("Connection")] [Description("The hostname or IP address of the PostgreSQL server to connect to.")] [DisplayName("Host")] [NpgsqlConnectionStringProperty("Server")] public string? Host { get => _host; set { _host = value; SetValue(nameof(Host), value); _dataSourceCached = null; } } string? _host; /// /// The TCP/IP port of the PostgreSQL server. /// [Category("Connection")] [Description("The TCP port of the PostgreSQL server.")] [DisplayName("Port")] [NpgsqlConnectionStringProperty] [DefaultValue(NpgsqlConnection.DefaultPort)] public int Port { get => _port; set { ArgumentOutOfRangeException.ThrowIfNegativeOrZero(value); _port = value; SetValue(nameof(Port), value); _dataSourceCached = null; } } int _port; /// /// The PostgreSQL database to connect to. /// [Category("Connection")] [Description("The PostgreSQL database to connect to.")] [DisplayName("Database")] [NpgsqlConnectionStringProperty("DB")] public string? Database { get => _database; set { _database = value; SetValue(nameof(Database), value); } } string? _database; /// /// The username to connect with. /// [Category("Connection")] [Description("The username to connect with.")] [DisplayName("Username")] [NpgsqlConnectionStringProperty("User Name", "UserId", "User Id", "UID")] public string? Username { get => _username; set { _username = value; SetValue(nameof(Username), value); } } string? _username; /// /// The password to connect with. /// [Category("Connection")] [Description("The password to connect with.")] [PasswordPropertyText(true)] [DisplayName("Password")] [NpgsqlConnectionStringProperty("PSW", "PWD")] public string? Password { get => _password; set { _password = value; SetValue(nameof(Password), value); } } string? _password; /// /// Path to a PostgreSQL password file (PGPASSFILE), from which the password would be taken. /// [Category("Connection")] [Description("Path to a PostgreSQL password file (PGPASSFILE), from which the password would be taken.")] [DisplayName("Passfile")] [NpgsqlConnectionStringProperty] public string? Passfile { get => _passfile; set { _passfile = value; SetValue(nameof(Passfile), value); } } string? _passfile; /// /// The optional application name parameter to be sent to the backend during connection initiation. /// [Category("Connection")] [Description("The optional application name parameter to be sent to the backend during connection initiation")] [DisplayName("Application Name")] [NpgsqlConnectionStringProperty] public string? ApplicationName { get => _applicationName; set { _applicationName = value; SetValue(nameof(ApplicationName), value); } } string? _applicationName; /// /// Whether to enlist in an ambient TransactionScope. /// [Category("Connection")] [Description("Whether to enlist in an ambient TransactionScope.")] [DisplayName("Enlist")] [DefaultValue(true)] [NpgsqlConnectionStringProperty] public bool Enlist { get => _enlist; set { _enlist = value; SetValue(nameof(Enlist), value); } } bool _enlist; /// /// Gets or sets the schema search path. /// [Category("Connection")] [Description("Gets or sets the schema search path.")] [DisplayName("Search Path")] [NpgsqlConnectionStringProperty] public string? SearchPath { get => _searchPath; set { _searchPath = value; SetValue(nameof(SearchPath), value); } } string? _searchPath; /// /// Gets or sets the client_encoding parameter. /// [Category("Connection")] [Description("Gets or sets the client_encoding parameter.")] [DisplayName("Client Encoding")] [NpgsqlConnectionStringProperty] public string? ClientEncoding { get => _clientEncoding; set { _clientEncoding = value; SetValue(nameof(ClientEncoding), value); } } string? _clientEncoding; /// /// Gets or sets the .NET encoding that will be used to encode/decode PostgreSQL string data. /// [Category("Connection")] [Description("Gets or sets the .NET encoding that will be used to encode/decode PostgreSQL string data.")] [DisplayName("Encoding")] [DefaultValue("UTF8")] [NpgsqlConnectionStringProperty] public string Encoding { get => _encoding; set { _encoding = value; SetValue(nameof(Encoding), value); } } string _encoding = "UTF8"; /// /// Gets or sets the PostgreSQL session timezone, in Olson/IANA database format. /// [Category("Connection")] [Description("Gets or sets the PostgreSQL session timezone, in Olson/IANA database format.")] [DisplayName("Timezone")] [NpgsqlConnectionStringProperty] public string? Timezone { get => _timezone; set { _timezone = value; SetValue(nameof(Timezone), value); } } string? _timezone; #endregion #region Properties - Security /// /// Controls whether SSL is required, disabled or preferred, depending on server support. /// [Category("Security")] [Description("Controls whether SSL is required, disabled or preferred, depending on server support.")] [DisplayName("SSL Mode")] [DefaultValue(SslMode.Prefer)] [NpgsqlConnectionStringProperty] public SslMode SslMode { get => _sslMode; set { _sslMode = value; SetValue(nameof(SslMode), value); } } SslMode _sslMode; /// /// Controls how SSL encryption is negotiated with the server, if SSL is used. /// [Category("Security")] [Description("Controls how SSL encryption is negotiated with the server, if SSL is used.")] [DisplayName("SSL Negotiation")] [NpgsqlConnectionStringProperty] public SslNegotiation SslNegotiation { get => UserProvidedSslNegotiation ?? SslNegotiation.Postgres; set { UserProvidedSslNegotiation = value; SetValue(nameof(SslNegotiation), value); } } internal SslNegotiation? UserProvidedSslNegotiation { get; private set; } /// /// Controls whether GSS encryption is required, disabled or preferred, depending on server support. /// [Category("Security")] [Description("Controls whether GSS encryption is required, disabled or preferred, depending on server support.")] [DisplayName("GSS Encryption Mode")] [NpgsqlConnectionStringProperty] public GssEncryptionMode GssEncryptionMode { get => UserProvidedGssEncMode ?? GssEncryptionMode.Prefer; set { UserProvidedGssEncMode = value; SetValue(nameof(GssEncryptionMode), value); } } internal GssEncryptionMode? UserProvidedGssEncMode { get; private set; } /// /// Location of a client certificate to be sent to the server. /// [Category("Security")] [Description("Location of a client certificate to be sent to the server.")] [DisplayName("SSL Certificate")] [NpgsqlConnectionStringProperty] public string? SslCertificate { get => _sslCertificate; set { _sslCertificate = value; SetValue(nameof(SslCertificate), value); } } string? _sslCertificate; /// /// Location of a client key for a client certificate to be sent to the server. /// [Category("Security")] [Description("Location of a client key for a client certificate to be sent to the server.")] [DisplayName("SSL Key")] [NpgsqlConnectionStringProperty] public string? SslKey { get => _sslKey; set { _sslKey = value; SetValue(nameof(SslKey), value); } } string? _sslKey; /// /// Password for a key for a client certificate. /// [Category("Security")] [Description("Password for a key for a client certificate.")] [DisplayName("SSL Password")] [NpgsqlConnectionStringProperty] public string? SslPassword { get => _sslPassword; set { _sslPassword = value; SetValue(nameof(SslPassword), value); } } string? _sslPassword; /// /// Location of a CA certificate used to validate the server certificate. /// [Category("Security")] [Description("Location of a CA certificate used to validate the server certificate.")] [DisplayName("Root Certificate")] [NpgsqlConnectionStringProperty] public string? RootCertificate { get => _rootCertificate; set { _rootCertificate = value; SetValue(nameof(RootCertificate), value); } } string? _rootCertificate; /// /// Whether to check the certificate revocation list during authentication. /// False by default. /// [Category("Security")] [Description("Whether to check the certificate revocation list during authentication.")] [DisplayName("Check Certificate Revocation")] [NpgsqlConnectionStringProperty] public bool CheckCertificateRevocation { get => _checkCertificateRevocation; set { _checkCertificateRevocation = value; SetValue(nameof(CheckCertificateRevocation), value); } } bool _checkCertificateRevocation; /// /// The Kerberos service name to be used for authentication. /// [Category("Security")] [Description("The Kerberos service name to be used for authentication.")] [DisplayName("Kerberos Service Name")] [NpgsqlConnectionStringProperty("Krbsrvname")] [DefaultValue("postgres")] public string KerberosServiceName { get => _kerberosServiceName; set { _kerberosServiceName = value; SetValue(nameof(KerberosServiceName), value); } } string _kerberosServiceName = "postgres"; /// /// The Kerberos realm to be used for authentication. /// [Category("Security")] [Description("The Kerberos realm to be used for authentication.")] [DisplayName("Include Realm")] [DefaultValue(true)] [NpgsqlConnectionStringProperty] public bool IncludeRealm { get => _includeRealm; set { _includeRealm = value; SetValue(nameof(IncludeRealm), value); } } bool _includeRealm; /// /// Gets or sets a Boolean value that indicates if security-sensitive information, such as the password, is not returned as part of the connection if the connection is open or has ever been in an open state. /// [Category("Security")] [Description("Gets or sets a Boolean value that indicates if security-sensitive information, such as the password, is not returned as part of the connection if the connection is open or has ever been in an open state.")] [DisplayName("Persist Security Info")] [NpgsqlConnectionStringProperty] public bool PersistSecurityInfo { get => _persistSecurityInfo; set { _persistSecurityInfo = value; SetValue(nameof(PersistSecurityInfo), value); } } bool _persistSecurityInfo; /// /// When enabled, parameter values are logged when commands are executed. Defaults to false. /// [Category("Security")] [Description("When enabled, parameter values are logged when commands are executed. Defaults to false.")] [DisplayName("Log Parameters")] [NpgsqlConnectionStringProperty] public bool LogParameters { get => _logParameters; set { _logParameters = value; SetValue(nameof(LogParameters), value); } } bool _logParameters; internal const string IncludeExceptionDetailDisplayName = "Include Error Detail"; /// /// When enabled, PostgreSQL error details are included on and /// . These can contain sensitive data. /// [Category("Security")] [Description("When enabled, PostgreSQL error and notice details are included on PostgresException.Detail and PostgresNotice.Detail. These can contain sensitive data.")] [DisplayName(IncludeExceptionDetailDisplayName)] [NpgsqlConnectionStringProperty] public bool IncludeErrorDetail { get => _includeErrorDetail; set { _includeErrorDetail = value; SetValue(nameof(IncludeErrorDetail), value); } } bool _includeErrorDetail; /// /// When enabled, failed statements are included on . /// [Category("Security")] [Description("When enabled, failed batched commands are included on NpgsqlException.BatchCommand.")] [DisplayName("Include Failed Batched Command")] [NpgsqlConnectionStringProperty] public bool IncludeFailedBatchedCommand { get => _includeFailedBatchedCommand; set { _includeFailedBatchedCommand = value; SetValue(nameof(IncludeFailedBatchedCommand), value); } } bool _includeFailedBatchedCommand; /// /// Controls whether channel binding is required, disabled or preferred, depending on server support. /// [Category("Security")] [Description("Controls whether channel binding is required, disabled or preferred, depending on server support.")] [DisplayName("Channel Binding")] [DefaultValue(ChannelBinding.Prefer)] [NpgsqlConnectionStringProperty] public ChannelBinding ChannelBinding { get => _channelBinding; set { _channelBinding = value; SetValue(nameof(ChannelBinding), value); } } ChannelBinding _channelBinding; /// /// Controls the available authentication methods. /// [Category("Security")] [Description("Controls the available authentication methods.")] [DisplayName("Require Auth")] [NpgsqlConnectionStringProperty] public string? RequireAuth { get => _requireAuth; set { RequireAuthModes = ParseAuthMode(value); _requireAuth = value; SetValue(nameof(RequireAuth), value); } } string? _requireAuth; internal RequireAuthMode RequireAuthModes { get; private set; } internal static RequireAuthMode ParseAuthMode(string? value) { var modes = value?.Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries); if (modes is not { Length: > 0 }) return RequireAuthMode.All; var isNegative = false; RequireAuthMode parsedModes = default; for (var i = 0; i < modes.Length; i++) { var mode = modes[i]; var modeToParse = mode.AsSpan(); if (mode.StartsWith('!')) { if (i > 0 && !isNegative) throw new ArgumentException("Mixing both positive and negative authentication methods is not supported"); modeToParse = modeToParse.Slice(1); isNegative = true; } else { if (i > 0 && isNegative) throw new ArgumentException("Mixing both positive and negative authentication methods is not supported"); } // Explicitly disallow 'All' as libpq doesn't have it if (!Enum.TryParse(modeToParse, out var parsedMode) || parsedMode == RequireAuthMode.All) throw new ArgumentException($"Unable to parse authentication method \"{modeToParse}\""); parsedModes |= parsedMode; } var allowedModes = isNegative ? (RequireAuthMode)(RequireAuthMode.All - parsedModes) : parsedModes; if (allowedModes == default) throw new ArgumentException($"No authentication method is allowed. Check \"{nameof(RequireAuth)}\" in connection string."); return allowedModes; } #endregion #region Properties - Pooling /// /// Whether connection pooling should be used. /// [Category("Pooling")] [Description("Whether connection pooling should be used.")] [DisplayName("Pooling")] [NpgsqlConnectionStringProperty] [DefaultValue(true)] public bool Pooling { get => _pooling; set { _pooling = value; SetValue(nameof(Pooling), value); } } bool _pooling; /// /// The minimum connection pool size. /// [Category("Pooling")] [Description("The minimum connection pool size.")] [DisplayName("Minimum Pool Size")] [NpgsqlConnectionStringProperty] [DefaultValue(0)] public int MinPoolSize { get => _minPoolSize; set { ArgumentOutOfRangeException.ThrowIfNegative(value); _minPoolSize = value; SetValue(nameof(MinPoolSize), value); } } int _minPoolSize; /// /// The maximum connection pool size. /// [Category("Pooling")] [Description("The maximum connection pool size.")] [DisplayName("Maximum Pool Size")] [NpgsqlConnectionStringProperty] [DefaultValue(100)] public int MaxPoolSize { get => _maxPoolSize; set { ArgumentOutOfRangeException.ThrowIfNegative(value); _maxPoolSize = value; SetValue(nameof(MaxPoolSize), value); } } int _maxPoolSize; /// /// The time to wait before closing idle connections in the pool if the count /// of all connections exceeds MinPoolSize. /// /// The time (in seconds) to wait. The default value is 300. [Category("Pooling")] [Description("The time to wait before closing unused connections in the pool if the count of all connections exceeds MinPoolSize.")] [DisplayName("Connection Idle Lifetime")] [NpgsqlConnectionStringProperty] [DefaultValue(300)] public int ConnectionIdleLifetime { get => _connectionIdleLifetime; set { _connectionIdleLifetime = value; SetValue(nameof(ConnectionIdleLifetime), value); } } int _connectionIdleLifetime; /// /// How many seconds the pool waits before attempting to prune idle connections that are beyond /// idle lifetime (. /// /// The interval (in seconds). The default value is 10. [Category("Pooling")] [Description("How many seconds the pool waits before attempting to prune idle connections that are beyond idle lifetime.")] [DisplayName("Connection Pruning Interval")] [NpgsqlConnectionStringProperty] [DefaultValue(10)] public int ConnectionPruningInterval { get => _connectionPruningInterval; set { _connectionPruningInterval = value; SetValue(nameof(ConnectionPruningInterval), value); } } int _connectionPruningInterval; /// /// The total maximum lifetime of connections (in seconds). Connections which have exceeded this value will be /// destroyed instead of returned from the pool. This is useful in clustered configurations to force load /// balancing between a running server and a server just brought online. It can also be useful to prevent /// runaway memory growth of connections at the PostgreSQL server side, because in some cases very long lived /// connections slowly consume more and more memory over time. /// Defaults to 3600 seconds (1 hour). /// /// The time (in seconds) to wait, or 0 to to make connections last indefinitely. [Category("Pooling")] [Description("The total maximum lifetime of connections (in seconds).")] [DisplayName("Connection Lifetime")] [NpgsqlConnectionStringProperty("Load Balance Timeout")] [DefaultValue(3600)] public int ConnectionLifetime { get => _connectionLifetime; set { _connectionLifetime = value; SetValue(nameof(ConnectionLifetime), value); } } int _connectionLifetime; #endregion #region Properties - Timeouts /// /// The time to wait (in seconds) while trying to establish a connection before terminating the attempt and generating an error. /// Defaults to 15 seconds. /// [Category("Timeouts")] [Description("The time to wait (in seconds) while trying to establish a connection before terminating the attempt and generating an error.")] [DisplayName("Timeout")] [NpgsqlConnectionStringProperty] [DefaultValue(DefaultTimeout)] public int Timeout { get => _timeout; set { ArgumentOutOfRangeException.ThrowIfNegative(value); ArgumentOutOfRangeException.ThrowIfGreaterThan(value, NpgsqlConnection.TimeoutLimit); _timeout = value; SetValue(nameof(Timeout), value); } } int _timeout; internal const int DefaultTimeout = 15; /// /// The time to wait (in seconds) while trying to execute a command before terminating the attempt and generating an error. /// Defaults to 30 seconds. /// [Category("Timeouts")] [Description("The time to wait (in seconds) while trying to execute a command before terminating the attempt and generating an error. Set to zero for infinity.")] [DisplayName("Command Timeout")] [NpgsqlConnectionStringProperty] [DefaultValue(NpgsqlCommand.DefaultTimeout)] public int CommandTimeout { get => _commandTimeout; set { ArgumentOutOfRangeException.ThrowIfNegative(value); _commandTimeout = value; SetValue(nameof(CommandTimeout), value); } } int _commandTimeout; /// /// The time to wait (in milliseconds) while trying to read a response for a cancellation request for a timed out or cancelled query, before terminating the attempt and generating an error. /// Zero for infinity, -1 to skip the wait. /// Defaults to 2000 milliseconds. /// [Category("Timeouts")] [Description("After Command Timeout is reached (or user supplied cancellation token is cancelled) and command cancellation is attempted, Npgsql waits for this additional timeout (in milliseconds) before breaking the connection. Defaults to 2000, set to zero for infinity.")] [DisplayName("Cancellation Timeout")] [NpgsqlConnectionStringProperty] [DefaultValue(2000)] public int CancellationTimeout { get => _cancellationTimeout; set { ArgumentOutOfRangeException.ThrowIfLessThan(value, -1); _cancellationTimeout = value; SetValue(nameof(CancellationTimeout), value); } } int _cancellationTimeout; #endregion #region Properties - Failover and load balancing /// /// Determines the preferred PostgreSQL target server type. /// [Category("Failover and load balancing")] [Description("Determines the preferred PostgreSQL target server type.")] [DisplayName("Target Session Attributes")] [NpgsqlConnectionStringProperty] public string? TargetSessionAttributes { get => TargetSessionAttributesParsed switch { Npgsql.TargetSessionAttributes.Any => "any", Npgsql.TargetSessionAttributes.Primary => "primary", Npgsql.TargetSessionAttributes.Standby => "standby", Npgsql.TargetSessionAttributes.PreferPrimary => "prefer-primary", Npgsql.TargetSessionAttributes.PreferStandby => "prefer-standby", Npgsql.TargetSessionAttributes.ReadWrite => "read-write", Npgsql.TargetSessionAttributes.ReadOnly => "read-only", null => null, _ => throw new ArgumentException($"Unhandled enum value '{TargetSessionAttributesParsed}'") }; set { TargetSessionAttributesParsed = value is null ? null : ParseTargetSessionAttributes(value.ToLowerInvariant()); SetValue(nameof(TargetSessionAttributes), value); } } internal TargetSessionAttributes? TargetSessionAttributesParsed { get; set; } internal static TargetSessionAttributes ParseTargetSessionAttributes(string s) => s switch { "any" => Npgsql.TargetSessionAttributes.Any, "primary" => Npgsql.TargetSessionAttributes.Primary, "standby" => Npgsql.TargetSessionAttributes.Standby, "prefer-primary" => Npgsql.TargetSessionAttributes.PreferPrimary, "prefer-standby" => Npgsql.TargetSessionAttributes.PreferStandby, "read-write" => Npgsql.TargetSessionAttributes.ReadWrite, "read-only" => Npgsql.TargetSessionAttributes.ReadOnly, _ => throw new ArgumentException($"TargetSessionAttributes contains an invalid value '{s}'") }; /// /// Enables balancing between multiple hosts by round-robin. /// [Category("Failover and load balancing")] [Description("Enables balancing between multiple hosts by round-robin.")] [DisplayName("Load Balance Hosts")] [NpgsqlConnectionStringProperty] public bool LoadBalanceHosts { get => _loadBalanceHosts; set { _loadBalanceHosts = value; SetValue(nameof(LoadBalanceHosts), value); } } bool _loadBalanceHosts; /// /// Controls for how long the host's cached state will be considered as valid. /// [Category("Failover and load balancing")] [Description("Controls for how long the host's cached state will be considered as valid.")] [DisplayName("Host Recheck Seconds")] [DefaultValue(10)] [NpgsqlConnectionStringProperty] public int HostRecheckSeconds { get => _hostRecheckSeconds; set { ArgumentOutOfRangeException.ThrowIfNegative(value); _hostRecheckSeconds = value; SetValue(nameof(HostRecheckSeconds), value); } } int _hostRecheckSeconds; #endregion Properties - Failover and load balancing #region Properties - Advanced /// /// The number of seconds of connection inactivity before Npgsql sends a keepalive query. /// Set to 0 (the default) to disable. /// [Category("Advanced")] [Description("The number of seconds of connection inactivity before Npgsql sends a keepalive query.")] [DisplayName("Keepalive")] [NpgsqlConnectionStringProperty] public int KeepAlive { get => _keepAlive; set { ArgumentOutOfRangeException.ThrowIfNegative(value); _keepAlive = value; SetValue(nameof(KeepAlive), value); } } int _keepAlive; /// /// Whether to use TCP keepalive with system defaults if overrides isn't specified. /// [Category("Advanced")] [Description("Whether to use TCP keepalive with system defaults if overrides isn't specified.")] [DisplayName("TCP Keepalive")] [NpgsqlConnectionStringProperty] public bool TcpKeepAlive { get => _tcpKeepAlive; set { _tcpKeepAlive = value; SetValue(nameof(TcpKeepAlive), value); } } bool _tcpKeepAlive; /// /// The number of seconds of connection inactivity before a TCP keepalive query is sent. /// Use of this option is discouraged, use instead if possible. /// Set to 0 (the default) to disable. /// [Category("Advanced")] [Description("The number of seconds of connection inactivity before a TCP keepalive query is sent.")] [DisplayName("TCP Keepalive Time")] [NpgsqlConnectionStringProperty] public int TcpKeepAliveTime { get => _tcpKeepAliveTime; set { ArgumentOutOfRangeException.ThrowIfNegative(value); _tcpKeepAliveTime = value; SetValue(nameof(TcpKeepAliveTime), value); } } int _tcpKeepAliveTime; /// /// The interval, in seconds, between when successive keep-alive packets are sent if no acknowledgement is received. /// Defaults to the value of . must be non-zero as well. /// [Category("Advanced")] [Description("The interval, in seconds, between when successive keep-alive packets are sent if no acknowledgement is received.")] [DisplayName("TCP Keepalive Interval")] [NpgsqlConnectionStringProperty] public int TcpKeepAliveInterval { get => _tcpKeepAliveInterval; set { ArgumentOutOfRangeException.ThrowIfNegative(value); _tcpKeepAliveInterval = value; SetValue(nameof(TcpKeepAliveInterval), value); } } int _tcpKeepAliveInterval; /// /// Determines the size of the internal buffer Npgsql uses when reading. Increasing may improve performance if transferring large values from the database. /// [Category("Advanced")] [Description("Determines the size of the internal buffer Npgsql uses when reading. Increasing may improve performance if transferring large values from the database.")] [DisplayName("Read Buffer Size")] [NpgsqlConnectionStringProperty] [DefaultValue(NpgsqlReadBuffer.DefaultSize)] public int ReadBufferSize { get => _readBufferSize; set { _readBufferSize = value; SetValue(nameof(ReadBufferSize), value); } } int _readBufferSize; /// /// Determines the size of the internal buffer Npgsql uses when writing. Increasing may improve performance if transferring large values to the database. /// [Category("Advanced")] [Description("Determines the size of the internal buffer Npgsql uses when writing. Increasing may improve performance if transferring large values to the database.")] [DisplayName("Write Buffer Size")] [NpgsqlConnectionStringProperty] [DefaultValue(NpgsqlWriteBuffer.DefaultSize)] public int WriteBufferSize { get => _writeBufferSize; set { _writeBufferSize = value; SetValue(nameof(WriteBufferSize), value); } } int _writeBufferSize; /// /// Determines the size of socket read buffer. /// [Category("Advanced")] [Description("Determines the size of socket receive buffer.")] [DisplayName("Socket Receive Buffer Size")] [NpgsqlConnectionStringProperty] public int SocketReceiveBufferSize { get => _socketReceiveBufferSize; set { _socketReceiveBufferSize = value; SetValue(nameof(SocketReceiveBufferSize), value); } } int _socketReceiveBufferSize; /// /// Determines the size of socket send buffer. /// [Category("Advanced")] [Description("Determines the size of socket send buffer.")] [DisplayName("Socket Send Buffer Size")] [NpgsqlConnectionStringProperty] public int SocketSendBufferSize { get => _socketSendBufferSize; set { _socketSendBufferSize = value; SetValue(nameof(SocketSendBufferSize), value); } } int _socketSendBufferSize; /// /// The maximum number SQL statements that can be automatically prepared at any given point. /// Beyond this number the least-recently-used statement will be recycled. /// Zero (the default) disables automatic preparation. /// [Category("Advanced")] [Description("The maximum number SQL statements that can be automatically prepared at any given point. Beyond this number the least-recently-used statement will be recycled. Zero (the default) disables automatic preparation.")] [DisplayName("Max Auto Prepare")] [NpgsqlConnectionStringProperty] public int MaxAutoPrepare { get => _maxAutoPrepare; set { ArgumentOutOfRangeException.ThrowIfNegative(value); _maxAutoPrepare = value; SetValue(nameof(MaxAutoPrepare), value); } } int _maxAutoPrepare; /// /// The minimum number of usages an SQL statement is used before it's automatically prepared. /// Defaults to 5. /// [Category("Advanced")] [Description("The minimum number of usages an SQL statement is used before it's automatically prepared. Defaults to 5.")] [DisplayName("Auto Prepare Min Usages")] [NpgsqlConnectionStringProperty] [DefaultValue(5)] public int AutoPrepareMinUsages { get => _autoPrepareMinUsages; set { ArgumentOutOfRangeException.ThrowIfNegativeOrZero(value); _autoPrepareMinUsages = value; SetValue(nameof(AutoPrepareMinUsages), value); } } int _autoPrepareMinUsages; /// /// If set to true, a pool connection's state won't be reset when it is closed (improves performance). /// Do not specify this unless you know what you're doing. /// [Category("Advanced")] [Description("If set to true, a pool connection's state won't be reset when it is closed (improves performance). Do not specify this unless you know what you're doing.")] [DisplayName("No Reset On Close")] [NpgsqlConnectionStringProperty] public bool NoResetOnClose { get => _noResetOnClose; set { _noResetOnClose = value; SetValue(nameof(NoResetOnClose), value); } } bool _noResetOnClose; /// /// Set the replication mode of the connection /// /// /// This property and its corresponding enum are intentionally kept internal as they /// should not be set by users or even be visible in their connection strings. /// Replication connections are a special kind of connection that is encapsulated in /// /// and . /// [NpgsqlConnectionStringProperty] [DisplayName("Replication Mode")] internal ReplicationMode ReplicationMode { get => _replicationMode; set { _replicationMode = value; SetValue(nameof(ReplicationMode), value); } } ReplicationMode _replicationMode; /// /// Set PostgreSQL configuration parameter default values for the connection. /// [Category("Advanced")] [Description("Set PostgreSQL configuration parameter default values for the connection.")] [DisplayName("Options")] [NpgsqlConnectionStringProperty] public string? Options { get => _options; set { _options = value; SetValue(nameof(Options), value); } } string? _options; /// /// Configure the way arrays of value types are returned when requested as object instances. /// [Category("Advanced")] [Description("Configure the way arrays of value types are returned when requested as object instances.")] [DisplayName("Array Nullability Mode")] [NpgsqlConnectionStringProperty] public ArrayNullabilityMode ArrayNullabilityMode { get => _arrayNullabilityMode; set { _arrayNullabilityMode = value; SetValue(nameof(ArrayNullabilityMode), value); } } ArrayNullabilityMode _arrayNullabilityMode; #endregion #region Multiplexing /// /// Enables multiplexing, which allows more efficient use of connections. /// [Category("Multiplexing")] [Description("Enables multiplexing, which allows more efficient use of connections.")] [DisplayName("Multiplexing")] [NpgsqlConnectionStringProperty] [DefaultValue(false)] public bool Multiplexing { get => _multiplexing; set { _multiplexing = value; SetValue(nameof(Multiplexing), value); } } bool _multiplexing; /// /// When multiplexing is enabled, determines the maximum number of outgoing bytes to buffer before /// flushing to the network. /// [Category("Multiplexing")] [Description("When multiplexing is enabled, determines the maximum number of outgoing bytes to buffer before " + "flushing to the network.")] [DisplayName("Write Coalescing Buffer Threshold Bytes")] [NpgsqlConnectionStringProperty] [DefaultValue(1000)] public int WriteCoalescingBufferThresholdBytes { get => _writeCoalescingBufferThresholdBytes; set { _writeCoalescingBufferThresholdBytes = value; SetValue(nameof(WriteCoalescingBufferThresholdBytes), value); } } int _writeCoalescingBufferThresholdBytes; #endregion #region Properties - Obsolete /// /// Load table composite type definitions, and not just free-standing composite types. /// [Category("Advanced")] [Description("Load table composite type definitions, and not just free-standing composite types.")] [DisplayName("Load Table Composites")] [NpgsqlConnectionStringProperty] [Obsolete("Specifying type loading options through the connection string is obsolete, use the DataSource builder instead. See the 9.0 release notes for more information.")] public bool LoadTableComposites { get => _loadTableComposites; set { _loadTableComposites = value; SetValue(nameof(LoadTableComposites), value); } } bool _loadTableComposites; /// /// A compatibility mode for special PostgreSQL server types. /// [Category("Compatibility")] [Description("A compatibility mode for special PostgreSQL server types.")] [DisplayName("Server Compatibility Mode")] [NpgsqlConnectionStringProperty] [Obsolete("Specifying type loading options through the connection string is obsolete, use the DataSource builder instead. See the 9.0 release notes for more information.")] public ServerCompatibilityMode ServerCompatibilityMode { // Physical replication connections don't allow regular queries, so we can't load types from PG get => ReplicationMode is ReplicationMode.Physical ? ServerCompatibilityMode.NoTypeLoading : _serverCompatibilityMode; set { _serverCompatibilityMode = value; SetValue(nameof(ServerCompatibilityMode), value); } } ServerCompatibilityMode _serverCompatibilityMode; /// /// Whether to trust the server certificate without validating it. /// [Category("Security")] [Description("Whether to trust the server certificate without validating it.")] [DisplayName("Trust Server Certificate")] [Obsolete("The TrustServerCertificate parameter is no longer needed and does nothing.")] [NpgsqlConnectionStringProperty] public bool TrustServerCertificate { get => _trustServerCertificate; set { _trustServerCertificate = value; SetValue(nameof(TrustServerCertificate), value); } } bool _trustServerCertificate; /// /// The time to wait (in seconds) while trying to execute a an internal command before terminating the attempt and generating an error. /// [Category("Obsolete")] [Description("The time to wait (in seconds) while trying to execute a an internal command before terminating the attempt and generating an error. -1 uses CommandTimeout, 0 means no timeout.")] [DisplayName("Internal Command Timeout")] [NpgsqlConnectionStringProperty] [DefaultValue(-1)] [Obsolete("The InternalCommandTimeout parameter is no longer needed and does nothing.")] public int InternalCommandTimeout { get => _internalCommandTimeout; set { if (value != 0 && value != -1 && value < NpgsqlConnector.MinimumInternalCommandTimeout) throw new ArgumentOutOfRangeException(nameof(value), value, $"InternalCommandTimeout must be >= {NpgsqlConnector.MinimumInternalCommandTimeout}, 0 (infinite) or -1 (use CommandTimeout)"); _internalCommandTimeout = value; SetValue(nameof(InternalCommandTimeout), value); } } int _internalCommandTimeout; #endregion #region Misc internal void PostProcessAndValidate() { ArgumentException.ThrowIfNullOrWhiteSpace(Host); if (Multiplexing && !Pooling) throw new ArgumentException("Pooling must be on to use multiplexing"); if (SslNegotiation == SslNegotiation.Direct && SslMode is not SslMode.Require and not SslMode.VerifyCA and not SslMode.VerifyFull) throw new ArgumentException("SSL Mode has to be Require or higher to be used with direct SSL Negotiation"); if (!Host.Contains(',')) { if (TargetSessionAttributesParsed is not null && TargetSessionAttributesParsed != Npgsql.TargetSessionAttributes.Any) { throw new NotSupportedException("Target Session Attributes other then Any is only supported with multiple hosts"); } // Support single host:port format in Host if (!IsUnixSocket(Host, Port, out _) && TrySplitHostPort(Host.AsSpan(), out var newHost, out var newPort)) { Host = newHost; Port = newPort; } } } internal string ToStringWithoutPassword() { var clone = Clone(); clone.Password = null; return clone.ToString(); } internal string ConnectionStringForMultipleHosts { get { var clone = Clone(); clone[nameof(TargetSessionAttributes)] = null; return clone.ConnectionString; } } internal NpgsqlConnectionStringBuilder Clone() => new(ConnectionString); internal static bool TrySplitHostPort(ReadOnlySpan originalHost, [NotNullWhen(true)] out string? host, out int port) { var portSeparator = originalHost.LastIndexOf(':'); if (portSeparator != -1) { var otherColon = originalHost.Slice(0, portSeparator).LastIndexOf(':'); var ipv6End = originalHost.LastIndexOf(']'); if (otherColon == -1 || portSeparator > ipv6End && otherColon < ipv6End) { port = int.Parse(originalHost.Slice(portSeparator + 1)); host = originalHost.Slice(0, portSeparator).ToString(); return true; } } port = -1; host = null; return false; } internal static bool IsUnixSocket(string host, int port, [NotNullWhen(true)] out string? socketPath, bool replaceForAbstract = true) { socketPath = null; if (string.IsNullOrEmpty(host)) return false; var isPathRooted = Path.IsPathRooted(host); if (host[0] == '@') { if (replaceForAbstract) host = $"\0{host.Substring(1)}"; isPathRooted = true; } if (isPathRooted) { socketPath = Path.Combine(host, $".s.PGSQL.{port}"); return true; } return false; } /// /// Determines whether the specified object is equal to the current object. /// public override bool Equals(object? obj) => obj is NpgsqlConnectionStringBuilder o && EquivalentTo(o); /// /// Hash function. /// /// public override int GetHashCode() => Host?.GetHashCode() ?? 0; #endregion #region IDictionary /// /// Gets an containing the keys of the . /// public new ICollection Keys { get { var result = new string[base.Keys.Count]; var i = 0; foreach (var key in base.Keys) result[i++] = (string)key; return result; } } /// /// Gets an containing the values in the . /// public new ICollection Values { get { var result = new object?[base.Keys.Count]; var i = 0; foreach (var key in base.Values) result[i++] = (object?)key; return result; } } /// /// Copies the elements of the to an Array, starting at a particular Array index. /// /// /// The one-dimensional Array that is the destination of the elements copied from . /// The Array must have zero-based indexing. /// /// /// The zero-based index in array at which copying begins. /// public void CopyTo(KeyValuePair[] array, int arrayIndex) { foreach (var kv in this) array[arrayIndex++] = kv; } /// /// Returns an enumerator that iterates through the . /// /// public IEnumerator> GetEnumerator() { foreach (var k in Keys) yield return new KeyValuePair(k, this[k]); } #endregion IDictionary #region ICustomTypeDescriptor /// [RequiresUnreferencedCode("PropertyDescriptor's PropertyType cannot be statically discovered.")] protected override void GetProperties(Hashtable propertyDescriptors) { // Tweak which properties are exposed via TypeDescriptor. This affects the VS DDEX // provider, for example. base.GetProperties(propertyDescriptors); var toRemove = new List(); foreach (var value in propertyDescriptors.Values) { var d = (PropertyDescriptor)value; var isConnectionStringProperty = false; var isObsolete = false; foreach (var attribute in d.Attributes) { if (attribute is NpgsqlConnectionStringPropertyAttribute) { isConnectionStringProperty = true; } else if (attribute is ObsoleteAttribute) { isObsolete = true; } } if (!isConnectionStringProperty || isObsolete) toRemove.Add(d); } foreach (var o in toRemove) propertyDescriptors.Remove(o.DisplayName); } #endregion } #region Attributes /// /// Marks on which participate in the connection /// string. Optionally holds a set of synonyms for the property. /// [AttributeUsage(AttributeTargets.Property)] sealed class NpgsqlConnectionStringPropertyAttribute : Attribute { /// /// Holds a list of synonyms for the property. /// public string[] Synonyms { get; } /// /// Creates a . /// public NpgsqlConnectionStringPropertyAttribute() => Synonyms = []; /// /// Creates a . /// public NpgsqlConnectionStringPropertyAttribute(params string[] synonyms) => Synonyms = synonyms; } #endregion #region Enums /// /// Specifies how to manage SSL. /// public enum SslMode { /// /// SSL is disabled. If the server requires SSL, the connection will fail. /// Disable, /// /// Prefer non-SSL connections if the server allows them, but allow SSL connections. /// Allow, /// /// Prefer SSL connections if the server allows them, but allow connections without SSL. /// Prefer, /// /// Fail the connection if the server doesn't support SSL. /// Require, /// /// Fail the connection if the server doesn't support SSL. Also verifies server certificate. /// VerifyCA, /// /// Fail the connection if the server doesn't support SSL. Also verifies server certificate with host's name. /// VerifyFull } /// /// Specifies how to initialize SSL session. /// public enum SslNegotiation { /// /// Perform PostgreSQL protocol negotiation. /// Postgres, /// /// Start SSL handshake directly after establishing the TCP/IP connection. /// Direct } /// /// Specifies how to manage GSS encryption. /// public enum GssEncryptionMode { /// /// GSS encryption is disabled. If the server requires GSS encryption, the connection will fail. /// Disable, /// /// Prefer GSS encrypted connections if the server allows them, but allow connections without GSS encryption. /// Prefer, /// /// Fail the connection if the server doesn't support GSS encryption. /// Require } /// /// Specifies how to manage channel binding. /// public enum ChannelBinding { /// /// Channel binding is disabled. If the server requires channel binding, the connection will fail. /// Disable, /// /// Prefer channel binding if the server allows it, but connect without it if not. /// Prefer, /// /// Fail the connection if the server doesn't support channel binding. /// Require } /// /// Specifies how the mapping of arrays of /// value types /// behaves with respect to nullability when they are requested via an API returning an . /// public enum ArrayNullabilityMode { /// /// Arrays of value types are always returned as non-nullable arrays (e.g. int[]). /// If the PostgreSQL array contains a NULL value, an exception is thrown. This is the default mode. /// Never, /// /// Arrays of value types are always returned as nullable arrays (e.g. int?[]). /// Always, /// /// The type of array that gets returned is determined at runtime. /// Arrays of value types are returned as non-nullable arrays (e.g. int[]) /// if the actual instance that gets returned doesn't contain null values /// and as nullable arrays (e.g. int?[]) if it does. /// /// When using this setting, make sure that your code is prepared to the fact /// that the actual type of array instances returned from APIs like /// may change on a row by row base. PerInstance, } /// /// Specifies whether the connection shall be initialized as a physical or /// logical replication connection /// /// /// This enum and its corresponding property are intentionally kept internal as they /// should not be set by users or even be visible in their connection strings. /// Replication connections are a special kind of connection that is encapsulated in /// /// and . /// enum ReplicationMode { /// /// Replication disabled. This is the default /// Off, /// /// Physical replication enabled /// Physical, /// /// Logical replication enabled /// Logical } /// /// Specifies which authentication methods are supported. /// [Flags] enum RequireAuthMode { /// /// Plaintext password. /// Password = 1, /// /// MD5 hashed password. /// MD5 = 2, /// /// Kerberos. /// GSS = 4, /// /// Windows SSPI. /// SSPI = 8, /// /// SASL. /// ScramSHA256 = 16, /// /// No authentication exchange. /// None = 32, /// /// All authentication methods. For internal use. /// All = Password | MD5 | GSS | SSPI | ScramSHA256 | None } #endregion
X Tutup