Add a SQLite database backend #265

Merged
wojtek merged 20 commits from 248---replace-json-file-as-a-database-with-sqlite into main 2025-01-12 10:24:53 +01:00
2 changed files with 26 additions and 24 deletions
Showing only changes of commit 65299269c4 - Show all commits

View File

@ -10,9 +10,9 @@ use crate::{
collection::album::AlbumDate, collection::album::AlbumDate,
external::database::{ external::database::{
serde::{ serde::{
common::{SerdeAlbumDate, V20250103}, common::SerdeAlbumDate,
deserialize::{DeserializeAlbum, DeserializeArtist, DeserializeDatabase}, deserialize::{DeserializeAlbum, DeserializeArtist},
serialize::{SerializeAlbum, SerializeArtist, SerializeDatabase}, serialize::{SerializeAlbum, SerializeArtist},
}, },
sql::{Error, ISqlDatabaseBackend, ISqlTransactionBackend}, sql::{Error, ISqlDatabaseBackend, ISqlTransactionBackend},
}, },
@ -151,29 +151,22 @@ impl ISqlTransactionBackend for SqlTransactionSqliteBackend<'_> {
Self::execute(&mut stmt, ()) Self::execute(&mut stmt, ())
} }
fn insert_database_version(&self, version: &SerializeDatabase<'_>) -> Result<(), Error> { fn insert_database_version(&self, version: &str) -> Result<(), Error> {
let mut stmt = self.prepare_cached( let mut stmt = self.prepare_cached(
"INSERT INTO database_metadata (name, value) "INSERT INTO database_metadata (name, value)
VALUES (?1, ?2)", VALUES (?1, ?2)",
)?; )?;
let version = match version {
SerializeDatabase::V20250103(_) => V20250103,
};
Self::execute(&mut stmt, ("version", version)) Self::execute(&mut stmt, ("version", version))
} }
fn select_database_version<'a>(&self) -> Result<DeserializeDatabase, Error> { fn select_database_version<'a>(&self) -> Result<Option<String>, Error> {
let mut stmt = let mut stmt =
self.prepare_cached("SELECT value FROM database_metadata WHERE name = 'version'")?; self.prepare_cached("SELECT value FROM database_metadata WHERE name = 'version'")?;
let mut rows = Self::query(&mut stmt, ())?; let mut rows = Self::query(&mut stmt, ())?;
match Self::next_row(&mut rows)? { Self::next_row(&mut rows)?
Some(row) => match Self::get_value::<String>(row, 0)?.as_str() { .map(|row| Self::get_value(row, 0))
V20250103 => Ok(DeserializeDatabase::V20250103(vec![])), .transpose()
s => Err(Error::SerDeError(format!("unknown database version: {s}"))),
},
None => Err(Error::SerDeError(String::from("missing database version"))),
}
} }
fn insert_artist(&self, artist: &SerializeArtist<'_>) -> Result<(), Error> { fn insert_artist(&self, artist: &SerializeArtist<'_>) -> Result<(), Error> {

View File

@ -13,6 +13,7 @@ use crate::{
interface::database::{IDatabase, LoadError, SaveError}, interface::database::{IDatabase, LoadError, SaveError},
}, },
external::database::serde::{ external::database::serde::{
common::V20250103,
deserialize::{DeserializeAlbum, DeserializeArtist, DeserializeDatabase}, deserialize::{DeserializeAlbum, DeserializeArtist, DeserializeDatabase},
serialize::{SerializeAlbum, SerializeArtist, SerializeDatabase}, serialize::{SerializeAlbum, SerializeArtist, SerializeDatabase},
}, },
@ -51,11 +52,10 @@ pub trait ISqlTransactionBackend {
fn drop_albums_table(&self) -> Result<(), Error>; fn drop_albums_table(&self) -> Result<(), Error>;
/// Set the database version. /// Set the database version.
#[allow(clippy::needless_lifetimes)] // Conflicts with automock. fn insert_database_version(&self, version: &str) -> Result<(), Error>;
fn insert_database_version<'a>(&self, version: &SerializeDatabase<'a>) -> Result<(), Error>;
/// Get the database version. /// Get the database version.
fn select_database_version(&self) -> Result<DeserializeDatabase, Error>; fn select_database_version(&self) -> Result<Option<String>, Error>;
/// Insert an artist into the artist table. /// Insert an artist into the artist table.
#[allow(clippy::needless_lifetimes)] // Conflicts with automock. #[allow(clippy::needless_lifetimes)] // Conflicts with automock.
@ -154,15 +154,24 @@ impl<SDB: for<'conn> ISqlDatabaseBackend<'conn>> IDatabase for SqlDatabase<SDB>
fn load(&mut self) -> Result<Collection, LoadError> { fn load(&mut self) -> Result<Collection, LoadError> {
let tx = self.backend.transaction()?; let tx = self.backend.transaction()?;
let mut database = tx.select_database_version()?; let version = tx
match database { .select_database_version()?
DeserializeDatabase::V20250103(ref mut coll) => { .ok_or_else(|| LoadError::SerDeError(String::from("missing database version")))?;
coll.extend(tx.select_all_artists()?);
let database = match version.as_str() {
V20250103 => {
let mut coll = tx.select_all_artists()?;
for artist in coll.iter_mut() { for artist in coll.iter_mut() {
artist.albums.extend(tx.select_artist_albums(&artist.name)?); artist.albums.extend(tx.select_artist_albums(&artist.name)?);
} }
DeserializeDatabase::V20250103(coll)
} }
} s => {
return Err(LoadError::SerDeError(format!(
"unknown database version: {s}"
)))
}
};
tx.commit()?; tx.commit()?;
Ok(database.into()) Ok(database.into())
@ -175,9 +184,9 @@ impl<SDB: for<'conn> ISqlDatabaseBackend<'conn>> IDatabase for SqlDatabase<SDB>
Self::drop_tables(&tx)?; Self::drop_tables(&tx)?;
Self::create_tables(&tx)?; Self::create_tables(&tx)?;
tx.insert_database_version(&database)?;
match database { match database {
SerializeDatabase::V20250103(artists) => { SerializeDatabase::V20250103(artists) => {
tx.insert_database_version(V20250103)?;
for artist in artists.iter() { for artist in artists.iter() {
tx.insert_artist(artist)?; tx.insert_artist(artist)?;
for album in artist.albums.iter() { for album in artist.albums.iter() {