123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310 |
- use super::build::{DerivedQuery, QueryComponent, QueryPart, StaticVersion};
- use super::{Filterable, Resolvable};
- use crate::entity::EntityColumn;
- use crate::model::Modelable;
- use crate::{Entity, Error, QueryInterface};
- use std::hash::{Hash, Hasher};
- use std::marker::PhantomData;
- 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,
- }
- }
- pub fn to(self, to: &'r T) -> Entire<'r, 'q, T> {
- Entire { wrap: self, to }
- }
- }
- 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) {
- "update".hash(hasher);
- std::any::TypeId::of::<T>().hash(hasher);
- }
- // next binding point is the first, we do nothing here
- 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 Output = ();
- type Table = T;
- }
- impl<'r, 'q, T: Entity> Resolvable<'r, 'q> for Update<'r, 'q, T> {
- type Output = ();
- fn qi(&self) -> &'r QueryInterface<'q> {
- self.qi
- }
- }
- pub struct Entire<'r, 'q, T: Entity> {
- wrap: Update<'r, 'q, T>,
- to: &'r T,
- }
- impl<'r, 'q, T: Entity> StaticVersion for Entire<'r, 'q, T> {
- type Is = Entire<'static, 'static, T>;
- }
- impl<'r, 'q, T: Entity> QueryComponent for Entire<'r, 'q, T> {
- fn derive(&self) -> DerivedQuery {
- let mut dq = self.wrap.derive();
- // skip ID column
- let cols = T::columns();
- for column in &cols[1..] {
- dq = dq.add(QueryPart::Set, format!("`{}` = ?", column.name()));
- }
- dq
- }
- fn contribute<H: Hasher>(&self, hasher: &mut H) {
- self.wrap.contribute(hasher);
- std::any::TypeId::of::<Self::Is>().hash(hasher);
- }
- fn bind(&self, stmt: &mut sqlite::Statement<'_>) -> Result<usize, Error> {
- let mut ind = self.wrap.bind(stmt)?;
- self.to.visit_values::<Error, _>(&mut |val| {
- val.bind_to(stmt, ind)?;
- ind += 1;
- Ok(())
- })?;
- Ok(ind)
- }
- }
- impl<'r, 'q, T: Entity> Resolvable<'r, 'q> for Entire<'r, 'q, T> {
- type Output = ();
- fn qi(&self) -> &'r QueryInterface<'q> {
- self.wrap.qi
- }
- }
- impl<'r, 'q, T: Entity> Filterable<'r, 'q> for Entire<'r, 'q, T> {
- type Output = ();
- type Table = T;
- }
- pub trait Settable<'r, 'q>: Resolvable<'r, 'q>
- 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>
- for Set<'r, 'q, S, C, G>
- where
- <S as StaticVersion>::Is: Settable<'static, 'static>,
- 'q: 'r,
- {
- type Output = ();
- 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 Output = ();
- type Table = C::Entity;
- }
- #[cfg(test)]
- mod test {
- use crate::prelude::*;
- use crate::query::Resolvable;
- 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"
- );
- }
- #[test]
- fn swapout() {
- let db = crate::DB::new_in_memory(crate::Schema::new().entity::<KVStore>()).unwrap();
- let qi = db.query_interface();
- let id = qi
- .add(&KVStore {
- key: "a".into(),
- value: "b".into(),
- })
- .unwrap();
- let check = qi.get().by(KVStore::ID, &id).all().unwrap();
- assert_eq!(check.len(), 1);
- assert_eq!(check[0].key, "a");
- assert_eq!(check[0].value, "b");
- qi.update()
- .to(&KVStore {
- key: "c".into(),
- value: "d".into(),
- })
- .by(KVStore::ID, &id)
- .exec()
- .unwrap();
- let check = qi.get().by(KVStore::ID, &id).all().unwrap();
- assert_eq!(check.len(), 1);
- assert_eq!(check[0].key, "c");
- assert_eq!(check[0].value, "d");
- let check = qi.get().by_id(&id).all().unwrap();
- assert_eq!(check.len(), 1);
- assert_eq!(check[0].key, "c");
- assert_eq!(check[0].value, "d");
- }
- }
|