Add automatic SQL script execution for empty database initialization

This commit is contained in:
2026-03-07 13:07:44 -05:00
parent c4127e7749
commit 75acefe989
2 changed files with 90 additions and 14 deletions
@@ -380,11 +380,89 @@ public sealed class PostgresDatabaseProvider : IJellyfinDatabaseProvider
var context = await DbContextFactory!.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
await using (context.ConfigureAwait(false))
{
// DISABLED for .NET 11 preview: EnsureCreatedAsync bypasses migrations and creates schema directly
// This prevents proper migration tracking. Use manual SQL scripts instead.
// await context.Database.EnsureCreatedAsync(cancellationToken).ConfigureAwait(false);
// Check if database is empty (no schema or no tables) and initialize if needed
var connection = context.Database.GetDbConnection();
var wasConnectionOpened = false;
logger.LogInformation("Database schema creation disabled for .NET 11 preview. Database must be initialized using SQL scripts in sql/schema_init/ directory.");
try
{
if (connection.State != System.Data.ConnectionState.Open)
{
await connection.OpenAsync(cancellationToken).ConfigureAwait(false);
wasConnectionOpened = true;
}
// Check if the library schema exists
var schemaCheckQuery = "SELECT COUNT(*) FROM information_schema.schemata WHERE schema_name = 'library'";
await using (var schemaCheckCommand = connection.CreateCommand())
{
schemaCheckCommand.CommandText = schemaCheckQuery;
var schemaExists = (long?)await schemaCheckCommand.ExecuteScalarAsync(cancellationToken).ConfigureAwait(false);
if (schemaExists == 0)
{
logger.LogInformation("Database is empty (library schema not found). Attempting to initialize from SQL script...");
// Try to load and execute create_database_schema.sql
var schemaScriptPath = Path.Combine(AppContext.BaseDirectory, "sql", "schema_init", "create_database_schema.sql");
if (File.Exists(schemaScriptPath))
{
logger.LogInformation("Found schema script at: {Path}", schemaScriptPath);
logger.LogInformation("Executing create_database_schema.sql to initialize database...");
try
{
var schemaScript = await File.ReadAllTextAsync(schemaScriptPath, cancellationToken).ConfigureAwait(false);
// Execute the schema creation script
await using (var schemaCommand = connection.CreateCommand())
{
schemaCommand.CommandText = schemaScript;
schemaCommand.CommandTimeout = 600; // 10 minutes for large schema creation
await schemaCommand.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false);
}
logger.LogInformation("Successfully initialized database from SQL script");
}
catch (Exception scriptEx)
{
logger.LogError(scriptEx, "Failed to execute schema creation script. You may need to run it manually: {Path}", schemaScriptPath);
throw;
}
}
else
{
logger.LogError("Database is empty but schema script not found at: {Path}", schemaScriptPath);
logger.LogError("Please create the database manually using the SQL script from sql/schema_init/create_database_schema.sql");
throw new FileNotFoundException($"Schema initialization script not found: {schemaScriptPath}");
}
}
}
// DISABLED for .NET 11 preview: EnsureCreatedAsync bypasses migrations and creates schema directly
// This prevents proper migration tracking. Use manual SQL scripts instead.
// await context.Database.EnsureCreatedAsync(cancellationToken).ConfigureAwait(false);
logger.LogInformation("Database schema verification complete.");
// Close connection before getting migrations to allow EF to manage connections
if (wasConnectionOpened && connection.State == System.Data.ConnectionState.Open)
{
await connection.CloseAsync().ConfigureAwait(false);
wasConnectionOpened = false;
}
}
catch
{
// Ensure connection is closed on error
if (wasConnectionOpened && connection.State == System.Data.ConnectionState.Open)
{
await connection.CloseAsync().ConfigureAwait(false);
}
throw;
}
// Get applied migrations
var appliedMigrations = await context.Database.GetAppliedMigrationsAsync(cancellationToken).ConfigureAwait(false);
@@ -446,17 +524,15 @@ public sealed class PostgresDatabaseProvider : IJellyfinDatabaseProvider
}
// Verify all expected tables exist in each schema
var connection = context.Database.GetDbConnection();
var wasOpened = false;
// Reopen connection for verification
if (connection.State != System.Data.ConnectionState.Open)
{
await connection.OpenAsync(cancellationToken).ConfigureAwait(false);
wasConnectionOpened = true;
}
try
{
if (connection.State != System.Data.ConnectionState.Open)
{
await connection.OpenAsync(cancellationToken).ConfigureAwait(false);
wasOpened = true;
}
foreach (var schema in Schemas.All)
{
// Count tables in the schema
@@ -490,7 +566,7 @@ public sealed class PostgresDatabaseProvider : IJellyfinDatabaseProvider
}
finally
{
if (wasOpened)
if (wasConnectionOpened && connection.State == System.Data.ConnectionState.Open)
{
await connection.CloseAsync().ConfigureAwait(false);
}