mirror of
https://github.com/rmcrackan/Libation.git
synced 2026-03-27 19:33:52 -04:00
Supporting postgres simplifies deployments to environments such as kubernetes. Since sqlite doesn't work well on nfs shares it can be easier for databases to have a dedicated db set up that applications can connect to. Sqlite is easier for most deployments though, so this will default to that if the settings haven't been updated to support it. This change does the following: - Separate out SQLite from the DataLayer and adds a Postgres assembly for migrations as well - Add a configuration setting for a postgres connection string that will be used if it is there, otherwise reverts to the original sqlite string - Add a copydb command for the cli to bootstrap the postgres db - A convenience script to update migrations for both dbs at the same time
123 lines
5.2 KiB
C#
123 lines
5.2 KiB
C#
using CommandLine;
|
|
using DataLayer;
|
|
using LibationFileManager;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using System;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace LibationCli
|
|
{
|
|
[Verb("copydb", HelpText = "Copy the local sqlite database to postgres.")]
|
|
public class CopyDbOptions : OptionsBase
|
|
{
|
|
[Option(shortName: 'c', longName: "connectionString")]
|
|
public string PostgresConnectionString { get; set; }
|
|
|
|
protected override async Task ProcessAsync()
|
|
{
|
|
var srcConnectionString = SqliteStorage.ConnectionString;
|
|
var destConnectionString = PostgresConnectionString ?? Configuration.Instance.PostgresqlConnectionString;
|
|
if (string.IsNullOrEmpty(destConnectionString))
|
|
{
|
|
Console.Error.WriteLine("Postgres connection string is not set. Please provide it using --connectionString or set it in the configuration.");
|
|
Environment.ExitCode = (int)ExitCode.RunTimeError;
|
|
return;
|
|
}
|
|
|
|
Console.WriteLine("Copying database to Postgres...");
|
|
Console.WriteLine("Source: " + srcConnectionString);
|
|
Console.WriteLine("Destination: " + destConnectionString);
|
|
Console.WriteLine();
|
|
|
|
using var source = LibationContextFactory.CreateSqlite(srcConnectionString);
|
|
using var destination = LibationContextFactory.CreatePostgres(destConnectionString);
|
|
|
|
await source.Database.MigrateAsync();
|
|
|
|
try
|
|
{
|
|
Console.WriteLine("Creating destination database...");
|
|
await destination.Database.MigrateAsync();
|
|
Console.WriteLine("Destination database recreated.");
|
|
Console.WriteLine();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.Error.WriteLine($"Error recreating destination database: {ex}");
|
|
Environment.ExitCode = (int)ExitCode.RunTimeError;
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
// Load all data from source with all navigation properties
|
|
// EF Core will track all relationships automatically
|
|
Console.WriteLine("Loading data from source database...");
|
|
|
|
var books = await source.Books
|
|
.Include(b => b.UserDefinedItem)
|
|
.Include(b => b.Supplements)
|
|
.ToListAsync();
|
|
Console.WriteLine($"Loaded {books.Count} books");
|
|
|
|
var libraryBooks = await source.LibraryBooks.ToListAsync();
|
|
Console.WriteLine($"Loaded {libraryBooks.Count} library books");
|
|
|
|
var contributors = await source.Contributors.ToListAsync();
|
|
Console.WriteLine($"Loaded {contributors.Count} contributors");
|
|
|
|
var series = await source.Series.ToListAsync();
|
|
Console.WriteLine($"Loaded {series.Count} series");
|
|
|
|
var categories = await source.Categories.ToListAsync();
|
|
Console.WriteLine($"Loaded {categories.Count} categories");
|
|
|
|
var categoryLadders = await source.CategoryLadders.ToListAsync();
|
|
Console.WriteLine($"Loaded {categoryLadders.Count} category ladders");
|
|
|
|
// Load junction tables explicitly
|
|
var bookContributors = await source.Set<BookContributor>().ToListAsync();
|
|
Console.WriteLine($"Loaded {bookContributors.Count} book-contributor links");
|
|
|
|
var seriesBooks = await source.Set<SeriesBook>().ToListAsync();
|
|
Console.WriteLine($"Loaded {seriesBooks.Count} series-book links");
|
|
|
|
var bookCategories = await source.Set<BookCategory>().ToListAsync();
|
|
Console.WriteLine($"Loaded {bookCategories.Count} book-category links");
|
|
|
|
Console.WriteLine();
|
|
Console.WriteLine("Copying data to destination database...");
|
|
|
|
// Add everything to destination context
|
|
// Order matters due to foreign keys: independent tables first
|
|
destination.Contributors.AddRange(contributors.Where(c => !c.IsEmpty));
|
|
destination.Series.AddRange(series);
|
|
destination.Categories.AddRange(categories);
|
|
destination.CategoryLadders.AddRange(categoryLadders);
|
|
destination.Books.AddRange(books);
|
|
destination.LibraryBooks.AddRange(libraryBooks);
|
|
|
|
// Add junction tables
|
|
destination.Set<BookContributor>().AddRange(bookContributors);
|
|
destination.Set<SeriesBook>().AddRange(seriesBooks);
|
|
destination.Set<BookCategory>().AddRange(bookCategories);
|
|
|
|
// Save all changes
|
|
await destination.SaveChangesAsync();
|
|
|
|
Console.WriteLine("All data copied successfully.");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.Error.WriteLine($"Error copying database: {ex}");
|
|
Environment.ExitCode = (int)ExitCode.RunTimeError;
|
|
return;
|
|
}
|
|
|
|
Console.WriteLine();
|
|
Console.WriteLine("Database copy completed successfully.");
|
|
}
|
|
}
|
|
}
|