|
@@ -19,6 +19,10 @@ impl<T: Entity> WithID<T> {
|
|
|
id: <T as Entity>::ID::from_raw_id(raw_id),
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ pub fn wrapped(self) -> T {
|
|
|
+ self.wrap
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
impl<T: Entity> WithID<T> {
|
|
@@ -96,6 +100,20 @@ impl<'l> QueryInterface<'l> {
|
|
|
res
|
|
|
}
|
|
|
|
|
|
+ /// Helper function to process an expected zero results
|
|
|
+ /// Note that this errors out if there is any result
|
|
|
+ fn expect_no_result(
|
|
|
+ &self,
|
|
|
+ stmt: &mut sqlite::Statement
|
|
|
+ ) -> Option<()> {
|
|
|
+ let state = stmt.next().ok()?;
|
|
|
+ if state != sqlite::State::Done{
|
|
|
+ return None;
|
|
|
+ }
|
|
|
+
|
|
|
+ Some(())
|
|
|
+ }
|
|
|
+
|
|
|
fn cached_query<Return>(
|
|
|
&self,
|
|
|
context: &'static str,
|
|
@@ -218,6 +236,112 @@ impl<'l> QueryInterface<'l> {
|
|
|
)
|
|
|
}
|
|
|
|
|
|
+ /// Delete entities by searching with a single property
|
|
|
+ pub fn delete_by<
|
|
|
+ T: Entity<Column = C>,
|
|
|
+ C: EntityColumns<Entity = T>,
|
|
|
+ V: crate::model::Modelable
|
|
|
+ >(
|
|
|
+ &self,
|
|
|
+ c: C,
|
|
|
+ val: V
|
|
|
+ ) -> Option<()> {
|
|
|
+ let table_name = <T as Entity>::table_name();
|
|
|
+ let column_name = <T as Entity>::name(c.clone());
|
|
|
+
|
|
|
+ self.cached_query_column(
|
|
|
+ "delete_by",
|
|
|
+ std::any::TypeId::of::<T>(),
|
|
|
+ &[c],
|
|
|
+ &|| {
|
|
|
+ let query = format!(
|
|
|
+ "DELETE FROM \"{}\" WHERE {} = ?",
|
|
|
+ table_name,
|
|
|
+ column_name);
|
|
|
+ self.db
|
|
|
+ .conn
|
|
|
+ .prepare(&query)
|
|
|
+ .expect(format!("Failed to prepare SQL query: {}", query).as_str())
|
|
|
+ },
|
|
|
+ &mut |stmt| {
|
|
|
+ val.bind_to(stmt, 1).ok()?;
|
|
|
+
|
|
|
+ self.expect_no_result(stmt)
|
|
|
+ },
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Delete entities by searching with a single property
|
|
|
+ pub fn delete_by_id<
|
|
|
+ I: EntityID<Entity = T>,
|
|
|
+ T: Entity<ID = I>,
|
|
|
+ >(
|
|
|
+ &self,
|
|
|
+ id: <T as Entity>::ID,
|
|
|
+ ) -> Option<()> {
|
|
|
+ let table_name = <T as Entity>::table_name();
|
|
|
+
|
|
|
+ self.cached_query(
|
|
|
+ "delete_by_id",
|
|
|
+ std::any::TypeId::of::<T>(),
|
|
|
+ &|| {
|
|
|
+ let query = format!(
|
|
|
+ "DELETE FROM \"{}\" WHERE id = ?",
|
|
|
+ table_name);
|
|
|
+ self.db
|
|
|
+ .conn
|
|
|
+ .prepare(&query)
|
|
|
+ .expect(format!("Failed to prepare SQL query: {}", query).as_str())
|
|
|
+ },
|
|
|
+ &mut |stmt| {
|
|
|
+ id.bind_to(stmt, 1).ok()?;
|
|
|
+
|
|
|
+ self.expect_no_result(stmt)
|
|
|
+ },
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Delete entities by searching with multiple properties
|
|
|
+ pub fn delete_by_multi<
|
|
|
+ T: Entity<Column = C>,
|
|
|
+ C: EntityColumns<Entity = T>,
|
|
|
+ >(
|
|
|
+ &self,
|
|
|
+ c: &[C],
|
|
|
+ val: &[&dyn crate::model::Modelable],
|
|
|
+ ) -> Option<()> {
|
|
|
+ let table_name = <T as Entity>::table_name();
|
|
|
+
|
|
|
+ assert_eq!(c.len(), val.len());
|
|
|
+
|
|
|
+ self.cached_query_column(
|
|
|
+ "delete_by_multi",
|
|
|
+ std::any::TypeId::of::<T>(),
|
|
|
+ c,
|
|
|
+ &|| {
|
|
|
+ let query = format!(
|
|
|
+ "DELETE FROM \"{}\" WHERE {}",
|
|
|
+ table_name,
|
|
|
+ c.iter()
|
|
|
+ .map(|col| format!("\"{}\" = ?", <T as Entity>::name(col.clone())))
|
|
|
+ .collect::<Vec<_>>()
|
|
|
+ .join(" AND ")
|
|
|
+ );
|
|
|
+ self.db
|
|
|
+ .conn
|
|
|
+ .prepare(&query)
|
|
|
+ .expect(format!("Failed to prepare SQL query: {}", query).as_str())
|
|
|
+ },
|
|
|
+ &mut |stmt| {
|
|
|
+ for index in 0..val.len() {
|
|
|
+ val[index].bind_to(stmt, index + 1).ok()?;
|
|
|
+ }
|
|
|
+
|
|
|
+ self.expect_no_result(stmt)
|
|
|
+ },
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
/// Search for an entity by ID
|
|
|
pub fn get_one_by_id<I: EntityID<Entity = T>, T: Entity>(&self, id: I) -> Option<WithID<T>> {
|
|
|
let table_name = <T as Entity>::table_name();
|