From d58f9adf019a2971c5b0e9b72f709b73688c201c Mon Sep 17 00:00:00 2001 From: solidDoWant Date: Mon, 29 Sep 2025 05:08:30 +0000 Subject: [PATCH] New: Extend Postgres support to enable all connection string options Signed-off-by: solidDoWant --- src/NzbDrone.Core.Test/Framework/DbTest.cs | 2 + .../Configuration/ConfigFileProvider.cs | 4 + .../Datastore/ConnectionStringFactory.cs | 84 +++++++++++++++++-- .../Datastore/PostgresOptions.cs | 2 + 4 files changed, 86 insertions(+), 6 deletions(-) diff --git a/src/NzbDrone.Core.Test/Framework/DbTest.cs b/src/NzbDrone.Core.Test/Framework/DbTest.cs index fba791b152..a27510a674 100644 --- a/src/NzbDrone.Core.Test/Framework/DbTest.cs +++ b/src/NzbDrone.Core.Test/Framework/DbTest.cs @@ -9,6 +9,7 @@ using Npgsql; using NUnit.Framework; using NzbDrone.Common.Extensions; +using NzbDrone.Common.Options; using NzbDrone.Core.Configuration; using NzbDrone.Core.Datastore; using NzbDrone.Core.Datastore.Migration.Framework; @@ -179,6 +180,7 @@ protected void SetupContainer() // Set up remaining container services Mocker.SetConstant(Options.Create(postgresOptions)); + Mocker.GetMock>().Setup(v => v.Value).Returns(new LogOptions()); Mocker.SetConstant(Mocker.Resolve()); Mocker.SetConstant(Mocker.Resolve()); Mocker.SetConstant(Mocker.Resolve()); diff --git a/src/NzbDrone.Core/Configuration/ConfigFileProvider.cs b/src/NzbDrone.Core/Configuration/ConfigFileProvider.cs index 1f88db6281..272666cee1 100644 --- a/src/NzbDrone.Core/Configuration/ConfigFileProvider.cs +++ b/src/NzbDrone.Core/Configuration/ConfigFileProvider.cs @@ -65,6 +65,8 @@ public interface IConfigFileProvider : IHandleAsync, string PostgresPassword { get; } string PostgresMainDb { get; } string PostgresLogDb { get; } + string PostgresMainDbConnectionString { get; } + string PostgresLogDbConnectionString { get; } string Theme { get; } bool TrustCgnatIpAddresses { get; } } @@ -251,6 +253,8 @@ public AuthenticationType AuthenticationMethod public string PostgresMainDb => _postgresOptions?.MainDb ?? GetValue("PostgresMainDb", "radarr-main", persist: false); public string PostgresLogDb => _postgresOptions?.LogDb ?? GetValue("PostgresLogDb", "radarr-log", persist: false); public int PostgresPort => (_postgresOptions?.Port ?? 0) != 0 ? _postgresOptions.Port : GetValueInt("PostgresPort", 5432, persist: false); + public string PostgresMainDbConnectionString => _postgresOptions?.MainDbConnectionString ?? GetValue("PostgresMainDbConnectionString", string.Empty, persist: false); + public string PostgresLogDbConnectionString => _postgresOptions?.LogDbConnectionString ?? GetValue("PostgresLogDbConnectionString", string.Empty, persist: false); public bool LogDbEnabled => _logOptions.DbEnabled ?? GetValueBoolean("LogDbEnabled", true, persist: false); public bool LogSql => _logOptions.Sql ?? GetValueBoolean("LogSql", false, persist: false); public int LogRotate => _logOptions.Rotate ?? GetValueInt("LogRotate", 50, persist: false); diff --git a/src/NzbDrone.Core/Datastore/ConnectionStringFactory.cs b/src/NzbDrone.Core/Datastore/ConnectionStringFactory.cs index 19c938737c..6f746d09aa 100644 --- a/src/NzbDrone.Core/Datastore/ConnectionStringFactory.cs +++ b/src/NzbDrone.Core/Datastore/ConnectionStringFactory.cs @@ -17,16 +17,32 @@ public interface IConnectionStringFactory public class ConnectionStringFactory : IConnectionStringFactory { private readonly IConfigFileProvider _configFileProvider; + private bool _usePostgres; + private bool _usePostgresConnectionStrings; public ConnectionStringFactory(IAppFolderInfo appFolderInfo, IConfigFileProvider configFileProvider) { _configFileProvider = configFileProvider; - MainDbConnection = _configFileProvider.PostgresHost.IsNotNullOrWhiteSpace() ? GetPostgresConnectionString(_configFileProvider.PostgresMainDb) : - GetConnectionString(appFolderInfo.GetDatabase()); + ValidatePostgresOptions(); - LogDbConnection = _configFileProvider.PostgresHost.IsNotNullOrWhiteSpace() ? GetPostgresConnectionString(_configFileProvider.PostgresLogDb) : - GetConnectionString(appFolderInfo.GetLogDatabase()); + if (_usePostgres) + { + if (_usePostgresConnectionStrings) + { + MainDbConnection = GetPostgresConnectionInfoFromConnectionString(_configFileProvider.PostgresMainDbConnectionString); + LogDbConnection = GetPostgresConnectionInfoFromConnectionString(_configFileProvider.PostgresLogDbConnectionString); + return; + } + + MainDbConnection = GetPostgresConnectionInfoFromIndividualValues(_configFileProvider.PostgresMainDb); + LogDbConnection = GetPostgresConnectionInfoFromIndividualValues(_configFileProvider.PostgresLogDb); + return; + } + + // Default to sqlite + MainDbConnection = GetConnectionString(appFolderInfo.GetDatabase()); + LogDbConnection = GetConnectionString(appFolderInfo.GetLogDatabase()); } public DatabaseConnectionInfo MainDbConnection { get; private set; } @@ -60,9 +76,9 @@ private static DatabaseConnectionInfo GetConnectionString(string dbPath) return new DatabaseConnectionInfo(DatabaseType.SQLite, connectionBuilder.ConnectionString); } - private DatabaseConnectionInfo GetPostgresConnectionString(string dbName) + private DatabaseConnectionInfo GetPostgresConnectionInfoFromIndividualValues(string dbName) { - var connectionBuilder = new NpgsqlConnectionStringBuilder + var connectionBuilder = new NpgsqlConnectionStringBuilder() { Database = dbName, Host = _configFileProvider.PostgresHost, @@ -74,5 +90,61 @@ private DatabaseConnectionInfo GetPostgresConnectionString(string dbName) return new DatabaseConnectionInfo(DatabaseType.PostgreSQL, connectionBuilder.ConnectionString); } + + private DatabaseConnectionInfo GetPostgresConnectionInfoFromConnectionString(string connectionString) + { + var connectionBuilder = new NpgsqlConnectionStringBuilder(connectionString) + { + Enlist = false + }; + + return new DatabaseConnectionInfo(DatabaseType.PostgreSQL, connectionBuilder.ConnectionString); + } + + /// + /// Validates that either Postgres connection strings are both set or neither are set, and that either connection strings or + /// other Postgres settings are set, but not both. + /// + /// Thrown when configuration is invalid. + private void ValidatePostgresOptions() + { + var isMainDBConnectionStringSet = !string.IsNullOrWhiteSpace(_configFileProvider.PostgresMainDbConnectionString); + var isLogDBConnectionStringSet = !string.IsNullOrWhiteSpace(_configFileProvider.PostgresLogDbConnectionString); + var isHostSet = !string.IsNullOrWhiteSpace(_configFileProvider.PostgresHost); + + if (!isHostSet && !isMainDBConnectionStringSet && !isLogDBConnectionStringSet) + { + // No Postgres settings are set, so nothing to validate + return; + } + + _usePostgres = true; + + if (_configFileProvider.LogDbEnabled) + { + if (!isMainDBConnectionStringSet && isLogDBConnectionStringSet) + { + throw new ArgumentException("Postgres MainDbConnectionString is set but LogDbConnectionString is not. Both must be set or neither."); + } + + if (isLogDBConnectionStringSet && !isMainDBConnectionStringSet) + { + throw new ArgumentException("Postgres LogDbConnectionString is set but MainDbConnectionString is not. Both must be set or neither."); + } + } + + // At this point either all required connection strings are set or neither, so only one needs to be checked + var areConnectionStringConfigsSet = isMainDBConnectionStringSet; + + // This one _must_ be set if connection strings are not being used, so it is used as a test to see if the user attempted configuration via individual settings + var areOtherPostgresConfigsSet = _configFileProvider.PostgresHost.IsNotNullOrWhiteSpace(); + + if (areConnectionStringConfigsSet && areOtherPostgresConfigsSet) + { + throw new ArgumentException($"Either both Postgres connection strings must be set, or the other Postgres settings must be set, but not both."); + } + + _usePostgresConnectionStrings = areConnectionStringConfigsSet; + } } } diff --git a/src/NzbDrone.Core/Datastore/PostgresOptions.cs b/src/NzbDrone.Core/Datastore/PostgresOptions.cs index 0c65a4b392..05bf7a5e1d 100644 --- a/src/NzbDrone.Core/Datastore/PostgresOptions.cs +++ b/src/NzbDrone.Core/Datastore/PostgresOptions.cs @@ -10,6 +10,8 @@ public class PostgresOptions public string Password { get; set; } public string MainDb { get; set; } public string LogDb { get; set; } + public string MainDbConnectionString { get; set; } + public string LogDbConnectionString { get; set; } public static PostgresOptions GetOptions() {