|
@@ -0,0 +1,183 @@
|
|
|
+use std::marker::PhantomData;
|
|
|
+use std::hash::{Hash,Hasher};
|
|
|
+use crate::{Entity,QueryInterface,Error};
|
|
|
+use super::build::{QueryComponent,StaticVersion,DerivedQuery,QueryPart};
|
|
|
+use super::{Filterable,Resolvable};
|
|
|
+use crate::entity::EntityColumn;
|
|
|
+use crate::model::Modelable;
|
|
|
+
|
|
|
+pub struct Update<'r, 'q, T: Entity> {
|
|
|
+ qi: &'r QueryInterface<'q>,
|
|
|
+ _ghost: PhantomData<T>,
|
|
|
+}
|
|
|
+
|
|
|
+impl<'r, 'q, T: Entity> Update<'r, 'q, T> {
|
|
|
+ pub fn new(qi: &'r QueryInterface<'q>) -> Self {
|
|
|
+ Self {
|
|
|
+ qi,
|
|
|
+ _ghost: std::marker::PhantomData,
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+impl<'r, 'q, T: Entity> Settable<'r, 'q> for Update<'r, 'q, T>
|
|
|
+where
|
|
|
+ 'q: 'r,
|
|
|
+{
|
|
|
+ type Table = T;
|
|
|
+}
|
|
|
+
|
|
|
+impl<'r, 'q, T: Entity> StaticVersion for Update<'r, 'q, T> {
|
|
|
+ type Is = Update<'static, 'static, T>;
|
|
|
+}
|
|
|
+
|
|
|
+impl<'r, 'q, T: Entity> QueryComponent for Update<'r, 'q, T> {
|
|
|
+ fn derive(&self) -> DerivedQuery {
|
|
|
+ DerivedQuery::new().add(
|
|
|
+ QueryPart::Root,
|
|
|
+ format!("UPDATE {}", T::table_name()),
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ fn contribute<H: Hasher>(&self, hasher: &mut H) {
|
|
|
+ "select".hash(hasher);
|
|
|
+ std::any::TypeId::of::<T>().hash(hasher);
|
|
|
+ }
|
|
|
+
|
|
|
+ // next binding point is the first
|
|
|
+ fn bind(&self, _stmt: &mut sqlite::Statement<'_>) -> Result<usize, Error> {
|
|
|
+ Ok(1)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl<'r, 'q, T: Entity> Filterable<'r, 'q> for Update<'r, 'q, T> {
|
|
|
+ type Table = T;
|
|
|
+}
|
|
|
+
|
|
|
+impl<'r, 'q, T: Entity> Resolvable<'r, 'q, T> for Update<'r, 'q, T> {
|
|
|
+ fn qi(&self) -> &'r QueryInterface<'q> {
|
|
|
+ self.qi
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+pub trait Settable<'r, 'q>: Resolvable<'r, 'q, Self::Table>
|
|
|
+where
|
|
|
+ 'q: 'r,
|
|
|
+{
|
|
|
+ type Table: Entity;
|
|
|
+
|
|
|
+ fn update<C: EntityColumn<Entity = Self::Table>, G: Modelable + ?Sized>(
|
|
|
+ self,
|
|
|
+ col: C,
|
|
|
+ given: &'r G,
|
|
|
+ ) -> Set<'r, 'q, Self, C, G>
|
|
|
+ where
|
|
|
+ Self: Sized
|
|
|
+ {
|
|
|
+ Set {
|
|
|
+ wrap: self,
|
|
|
+ col,
|
|
|
+ given,
|
|
|
+ _ghost: PhantomData
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/// A concrete SET clause
|
|
|
+pub struct Set<'r, 'q, S: Settable<'r, 'q>, C: EntityColumn, G: Modelable + ?Sized>
|
|
|
+where
|
|
|
+ 'q: 'r,
|
|
|
+{
|
|
|
+ wrap: S,
|
|
|
+ col: C,
|
|
|
+ given: &'r G,
|
|
|
+ _ghost: PhantomData<&'q ()>,
|
|
|
+}
|
|
|
+
|
|
|
+impl<'r, 'q, S: Settable<'r, 'q>, C: EntityColumn, G: Modelable + ?Sized> StaticVersion
|
|
|
+ for Set<'r, 'q, S, C, G>
|
|
|
+where
|
|
|
+ <S as StaticVersion>::Is: Settable<'static, 'static>,
|
|
|
+{
|
|
|
+ type Is = Set<'static, 'static, <S as StaticVersion>::Is, C, u64>;
|
|
|
+}
|
|
|
+
|
|
|
+impl<'r, 'q, S: Settable<'r, 'q>, C: EntityColumn, G: Modelable + ?Sized> QueryComponent
|
|
|
+ for Set<'r, 'q, S, C, G>
|
|
|
+where
|
|
|
+ <S as StaticVersion>::Is: Settable<'static, 'static>,
|
|
|
+ 'q: 'r,
|
|
|
+{
|
|
|
+ fn derive(&self) -> DerivedQuery {
|
|
|
+ self.wrap
|
|
|
+ .derive()
|
|
|
+ .add(QueryPart::Set, format!("{} = ?", self.col.name()))
|
|
|
+ }
|
|
|
+
|
|
|
+ fn contribute<H: Hasher>(&self, hasher: &mut H) {
|
|
|
+ self.wrap.contribute(hasher);
|
|
|
+ std::any::TypeId::of::<Self::Is>().hash(hasher);
|
|
|
+ std::any::TypeId::of::<C>().hash(hasher);
|
|
|
+ }
|
|
|
+
|
|
|
+ fn bind(&self, stmt: &mut sqlite::Statement<'_>) -> Result<usize, crate::Error> {
|
|
|
+ let next_index = self.wrap.bind(stmt)?;
|
|
|
+
|
|
|
+ self.given.bind_to(stmt, next_index)?;
|
|
|
+
|
|
|
+ Ok(next_index + 1)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl<'r, 'q, S: Settable<'r, 'q>, C: EntityColumn, G: Modelable + ?Sized>
|
|
|
+ Resolvable<'r, 'q, C::Entity> for Set<'r, 'q, S, C, G>
|
|
|
+where
|
|
|
+ <S as StaticVersion>::Is: Settable<'static, 'static>,
|
|
|
+ 'q: 'r,
|
|
|
+{
|
|
|
+ fn qi(&self) -> &'r QueryInterface<'q> {
|
|
|
+ self.wrap.qi()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl<'r, 'q, S: Settable<'r, 'q>, C: EntityColumn, G: Modelable + ?Sized>
|
|
|
+ Settable<'r, 'q> for Set<'r, 'q, S, C, G>
|
|
|
+where
|
|
|
+ <S as StaticVersion>::Is: Settable<'static, 'static>,
|
|
|
+ 'q: 'r,
|
|
|
+{
|
|
|
+ type Table = C::Entity;
|
|
|
+}
|
|
|
+
|
|
|
+impl<'r, 'q, S: Settable<'r, 'q>, C: EntityColumn, G: Modelable + ?Sized>
|
|
|
+ Filterable<'r, 'q> for Set<'r, 'q, S, C, G>
|
|
|
+where
|
|
|
+ <S as StaticVersion>::Is: Settable<'static, 'static>,
|
|
|
+ 'q: 'r,
|
|
|
+{
|
|
|
+ type Table = C::Entity;
|
|
|
+}
|
|
|
+
|
|
|
+#[cfg(test)]
|
|
|
+mod test {
|
|
|
+ use crate::prelude::*;
|
|
|
+ use crate::test_support::KVStore;
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn simple_update() {
|
|
|
+ let db = crate::DB::new_in_memory(crate::Schema::new().entity::<KVStore>()).unwrap();
|
|
|
+ let qi = db.query_interface();
|
|
|
+
|
|
|
+ qi.add(&KVStore { key: "key".into(), value: "value".into() }).unwrap();
|
|
|
+ qi.add(&KVStore { key: "key2".into(), value: "value2".into() }).unwrap();
|
|
|
+ qi.add(&KVStore { key: "key2".into(), value: "value2b".into() }).unwrap();
|
|
|
+
|
|
|
+ assert_eq!(qi.get().by(KVStore::Key, "key").one().unwrap().unwrap().value, "value");
|
|
|
+ assert_eq!(qi.get().by(KVStore::Key, "key2").all().unwrap().len(), 2);
|
|
|
+
|
|
|
+ qi.update().update(KVStore::Value, "newvalue").by(KVStore::Key, "key").exec().unwrap();
|
|
|
+ assert_eq!(qi.get().by(KVStore::Key, "key").one().unwrap().unwrap().value, "newvalue");
|
|
|
+ }
|
|
|
+}
|