Use psql subprocess to execute schema script - handles psql meta-commands

This commit is contained in:
2026-03-08 11:13:39 -04:00
parent 7657caa960
commit d4426fe0f6
2 changed files with 62 additions and 11 deletions
@@ -8,6 +8,7 @@ namespace Jellyfin.Database.Providers.Postgres;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
@@ -418,24 +419,74 @@ public sealed class PostgresDatabaseProvider : IJellyfinDatabaseProvider
try
{
var schemaScript = await File.ReadAllTextAsync(schemaScriptPath, cancellationToken).ConfigureAwait(false);
// The SQL file contains psql meta-commands (\restrict, etc.) that can't be executed via ExecuteNonQuery
// We need to use psql subprocess instead
var connectionString = connection.ConnectionString;
var builder = new NpgsqlConnectionStringBuilder(connectionString);
// Split script by statement terminator and execute each part
// PostgreSQL dump scripts may have multiple statements
await using (var command = connection.CreateCommand())
// Build psql command
var psqlArgs = $"-h {builder.Host} -p {builder.Port} -U {builder.Username} -d {builder.Database} -f \"{schemaScriptPath}\"";
logger.LogInformation("Executing via psql: psql {Args}", psqlArgs.Replace(builder.Password ?? string.Empty, "***", StringComparison.Ordinal));
var psqlProcess = new System.Diagnostics.Process
{
command.CommandText = schemaScript;
command.CommandTimeout = 600; // 10 minutes
await command.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false);
StartInfo = new System.Diagnostics.ProcessStartInfo
{
FileName = "psql",
Arguments = psqlArgs,
RedirectStandardOutput = true,
RedirectStandardError = true,
RedirectStandardInput = true,
UseShellExecute = false,
CreateNoWindow = true
}
};
// Set password via environment variable
if (!string.IsNullOrEmpty(builder.Password))
{
psqlProcess.StartInfo.EnvironmentVariables["PGPASSWORD"] = builder.Password;
}
logger.LogInformation("✅ Successfully initialized database from SQL script");
logger.LogInformation("Database is ready. You can now start Jellyfin.");
psqlProcess.Start();
// Read output
var output = await psqlProcess.StandardOutput.ReadToEndAsync(cancellationToken).ConfigureAwait(false);
var error = await psqlProcess.StandardError.ReadToEndAsync(cancellationToken).ConfigureAwait(false);
await psqlProcess.WaitForExitAsync(cancellationToken).ConfigureAwait(false);
if (psqlProcess.ExitCode == 0)
{
logger.LogInformation("✅ Successfully initialized database from SQL script via psql");
logger.LogInformation("Database is ready. You can now start Jellyfin.");
if (!string.IsNullOrWhiteSpace(output))
{
logger.LogDebug("psql output: {Output}", output);
}
}
else
{
logger.LogError("psql exited with code {ExitCode}", psqlProcess.ExitCode);
if (!string.IsNullOrWhiteSpace(error))
{
logger.LogError("psql error: {Error}", error);
}
throw new InvalidOperationException($"psql failed with exit code {psqlProcess.ExitCode}. Error: {error}");
}
}
catch (System.ComponentModel.Win32Exception psqlNotFound)
{
// psql not found in PATH - fallback to manual instructions
logger.LogWarning(psqlNotFound, "psql command not found in PATH. Cannot automatically initialize database.");
logger.LogError("Please run the SQL script manually: psql -U postgres -d jellyfin_testdata -f {Path}", schemaScriptPath);
throw new FileNotFoundException("psql command not found. Please install PostgreSQL client tools and ensure psql is in your PATH, or run the SQL script manually.", psqlNotFound);
}
catch (Exception scriptEx)
{
logger.LogError(scriptEx, "Failed to execute schema creation script");
logger.LogError("Please run the SQL script manually: psql -U jellyfin -d jellyfin -f {Path}", schemaScriptPath);
logger.LogError("Please run the SQL script manually: psql -U postgres -d jellyfin_testdata -f {Path}", schemaScriptPath);
throw;
}
}