search database has to be a file, as in-memory dbs are per connection
Some checks failed
Build Multiarch Container Image / call-reusable-workflow (push) Has been cancelled

This commit is contained in:
Sebastian Hugentobler 2024-06-26 16:14:58 +02:00
parent 97cf9db9ff
commit b8ed5b1cdf
Signed by: shu
GPG Key ID: BB32CF3CA052C2F0
7 changed files with 72 additions and 7 deletions

50
Cargo.lock generated
View File

@ -201,6 +201,7 @@ dependencies = [
"r2d2_sqlite", "r2d2_sqlite",
"rusqlite", "rusqlite",
"serde", "serde",
"tempfile",
"thiserror", "thiserror",
"time", "time",
] ]
@ -452,6 +453,16 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "errno"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
dependencies = [
"libc",
"windows-sys 0.52.0",
]
[[package]] [[package]]
name = "fallible-iterator" name = "fallible-iterator"
version = "0.3.0" version = "0.3.0"
@ -464,6 +475,12 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
[[package]]
name = "fastrand"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
[[package]] [[package]]
name = "flate2" name = "flate2"
version = "1.0.30" version = "1.0.30"
@ -887,9 +904,15 @@ dependencies = [
"vcpkg", "vcpkg",
] ]
[[package]]
name = "linux-raw-sys"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
[[package]] [[package]]
name = "little-hesinde" name = "little-hesinde"
version = "0.2.1" version = "0.2.3"
dependencies = [ dependencies = [
"calibre-db", "calibre-db",
"clap", "clap",
@ -1435,6 +1458,19 @@ version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
[[package]]
name = "rustix"
version = "0.38.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
dependencies = [
"bitflags 2.5.0",
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.52.0",
]
[[package]] [[package]]
name = "ryu" name = "ryu"
version = "1.0.17" version = "1.0.17"
@ -1657,6 +1693,18 @@ dependencies = [
"futures-core", "futures-core",
] ]
[[package]]
name = "tempfile"
version = "3.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1"
dependencies = [
"cfg-if",
"fastrand",
"rustix",
"windows-sys 0.52.0",
]
[[package]] [[package]]
name = "tera" name = "tera"
version = "1.19.1" version = "1.19.1"

View File

@ -19,5 +19,6 @@ FROM scratch
COPY --from=builder /app /app COPY --from=builder /app /app
CMD ["/app", "--listen-address", "[::]:3000", "--", "/library"] CMD ["/app", "--listen-address", "[::]:3000", "--", "/library"]
ENV TMPDIR=/
VOLUME ["/library"] VOLUME ["/library"]
EXPOSE 3000 EXPOSE 3000

View File

@ -12,5 +12,6 @@ r2d2 = "0.8.10"
r2d2_sqlite = "0.24.0" r2d2_sqlite = "0.24.0"
rusqlite = { version = "0.31.0", features = ["bundled", "time"] } rusqlite = { version = "0.31.0", features = ["bundled", "time"] }
serde = { workspace = true } serde = { workspace = true }
tempfile = "3.10.1"
thiserror = { workspace = true } thiserror = { workspace = true }
time = { workspace = true } time = { workspace = true }

View File

@ -1,5 +1,8 @@
//! Error handling for calibre database access. //! Error handling for calibre database access.
use std::io;
use tempfile::PersistError;
use thiserror::Error; use thiserror::Error;
use time::error::Parse; use time::error::Parse;
@ -16,9 +19,15 @@ pub enum DataStoreError {
/// Error connecting to the database. /// Error connecting to the database.
#[error("connection error")] #[error("connection error")]
ConnectionError(#[from] r2d2::Error), ConnectionError(#[from] r2d2::Error),
/// Error wparsing a datetime from the database. /// Error parsing a datetime from the database.
#[error("failed to parse datetime")] #[error("failed to parse datetime")]
DateTimeError(#[from] Parse), DateTimeError(#[from] Parse),
/// Error creating the search database.
#[error("failed to create search database")]
SearchDbError(#[from] io::Error),
/// Error marking the search database as persistent.
#[error("failed to persist search database")]
PersistSearchDbError(#[from] PersistError),
} }
/// Convert an SQLite error into a proper NoResults one if the query /// Convert an SQLite error into a proper NoResults one if the query

View File

@ -1,13 +1,14 @@
//! Provide search funcitonality for calibre. //! Provide search funcitonality for calibre.
//! //!
//! Because the calibre database can not be disturbed (it is treated as read-only) //! Because the calibre database can not be disturbed (it is treated as read-only)
//! it attaches an in-memory database and inserts the relevant data into a //! it attaches a temporary database and inserts the relevant data into a
//! virtual table leveraging fts5 (https://www.sqlite.org/fts5.html). Full-text search is run on //! virtual table leveraging fts5 (https://www.sqlite.org/fts5.html). Full-text search is run on
//! that virtual table. //! that virtual table.
use r2d2::{Pool, PooledConnection}; use r2d2::{Pool, PooledConnection};
use r2d2_sqlite::SqliteConnectionManager; use r2d2_sqlite::SqliteConnectionManager;
use rusqlite::named_params; use rusqlite::named_params;
use tempfile::NamedTempFile;
use crate::data::{book::Book, error::DataStoreError}; use crate::data::{book::Book, error::DataStoreError};
@ -31,11 +32,16 @@ const SEARCH_INIT_QUERY: &str = "INSERT INTO search.fts(book_id, data)
LEFT JOIN main.series AS s ON b2s.series = s.id LEFT JOIN main.series AS s ON b2s.series = s.id
GROUP BY b.id"; GROUP BY b.id";
/// Attach the fts in-memory database to the read-only calibre database. /// Attach the fts temporary database to the read-only calibre database.
pub(crate) fn attach(pool: &Pool<SqliteConnectionManager>) -> Result<(), DataStoreError> { pub(crate) fn attach(pool: &Pool<SqliteConnectionManager>) -> Result<(), DataStoreError> {
let conn = pool.get()?; let conn = pool.get()?;
let tmpfile = NamedTempFile::new()?;
let (_, path) = tmpfile.keep()?;
conn.execute("ATTACH DATABASE ':memory:' AS search", [])?; conn.execute(
&format!("ATTACH DATABASE '{}' AS search", path.to_string_lossy()),
[],
)?;
init(&conn)?; init(&conn)?;
Ok(()) Ok(())

View File

@ -1,6 +1,6 @@
[package] [package]
name = "little-hesinde" name = "little-hesinde"
version = "0.2.1" version = "0.2.3"
edition = "2021" edition = "2021"
license = { workspace = true } license = { workspace = true }
authors = { workspace = true } authors = { workspace = true }

View File

@ -76,7 +76,7 @@ pub mod opds {
pub mod templates; pub mod templates;
pub const APP_NAME: &str = "little-hesinde"; pub const APP_NAME: &str = "little-hesinde";
pub const VERSION: &str = "0.2.1"; pub const VERSION: &str = "0.2.3";
/// Internal marker data in lieu of a proper `Accept` header. /// Internal marker data in lieu of a proper `Accept` header.
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]