mithril_persistence/sqlite/
query.rs1use super::{SqLiteEntity, WhereCondition};
2
3pub trait Query {
9 type Entity: SqLiteEntity;
11
12 fn filters(&self) -> WhereCondition;
14
15 fn get_definition(&self, condition: &str) -> String;
17}
18
19#[cfg(test)]
20mod tests {
21 use sqlite::{Connection, Value};
22
23 use crate::sqlite::{
24 ConnectionExtensions, GetAllCondition, Projection, SourceAlias, SqliteConnection,
25 };
26
27 use super::super::{SqLiteEntity, entity::HydrationError};
28 use super::*;
29
30 #[derive(Debug, PartialEq)]
31 struct TestEntity {
32 text_data: String,
33 real_data: f64,
34 integer_data: i64,
35 maybe_null: Option<i64>,
36 }
37
38 impl SqLiteEntity for TestEntity {
39 fn hydrate(row: sqlite::Row) -> Result<Self, HydrationError> {
40 Ok(TestEntity {
41 text_data: row.read::<&str, _>(0).to_string(),
42 real_data: row.read::<f64, _>(1),
43 integer_data: row.read::<i64, _>(2),
44 maybe_null: row.read::<Option<i64>, _>(3),
45 })
46 }
47
48 fn get_projection() -> Projection {
49 let mut projection = Projection::default();
50
51 projection.add_field("text_data", "{:test:}.text_data", "text");
52 projection.add_field("real_data", "{:test:}.real_data", "real");
53 projection.add_field("integer_data", "{:test:}.integer_data", "integer");
54 projection.add_field("maybe_null", "{:test:}.maybe_null", "integer");
55
56 projection
57 }
58 }
59
60 struct GetTestEntityQuery {
61 condition: WhereCondition,
62 }
63
64 impl GetTestEntityQuery {
65 pub fn new(condition: WhereCondition) -> Self {
66 Self { condition }
67 }
68 }
69
70 impl Query for GetTestEntityQuery {
71 type Entity = TestEntity;
72
73 fn filters(&self) -> WhereCondition {
74 self.condition.clone()
75 }
76
77 fn get_definition(&self, condition: &str) -> String {
78 let aliases = SourceAlias::new(&[("{:test:}", "test")]);
79 let projection = Self::Entity::get_projection().expand(aliases);
80
81 format!("select {projection} from query_test as test where {condition}")
82 }
83 }
84
85 struct UpdateTestEntityQuery {
86 condition: WhereCondition,
87 }
88
89 impl UpdateTestEntityQuery {
90 pub fn new(condition: WhereCondition) -> Self {
91 Self { condition }
92 }
93 }
94
95 impl Query for UpdateTestEntityQuery {
96 type Entity = TestEntity;
97
98 fn filters(&self) -> WhereCondition {
99 self.condition.clone()
100 }
101
102 fn get_definition(&self, _condition: &str) -> String {
103 let aliases = SourceAlias::new(&[("{:test:}", "query_test")]);
104 let projection = Self::Entity::get_projection().expand(aliases);
105
106 format!(
107 r#"
108insert into query_test (text_data, real_data, integer_data, maybe_null) values (?1, ?2, ?3, ?4)
109 on conflict (text_data) do update set
110 real_data = excluded.real_data,
111 integer_data = excluded.integer_data,
112 maybe_null = excluded.maybe_null
113returning {projection}
114"#
115 )
116 }
117 }
118
119 impl GetAllCondition for GetTestEntityQuery {}
120
121 fn init_database() -> SqliteConnection {
122 let connection = Connection::open_thread_safe(":memory:").unwrap();
123 connection
124 .execute(
125 "
126 drop table if exists query_test;
127 create table query_test(text_data text not null primary key, real_data real not null, integer_data integer not null, maybe_null integer);
128 insert into query_test(text_data, real_data, integer_data, maybe_null) values ('row 1', 1.23, -52, null);
129 insert into query_test(text_data, real_data, integer_data, maybe_null) values ('row 2', 2.34, 1789, 0);
130 ",
131 )
132 .unwrap();
133
134 connection
135 }
136
137 #[test]
138 pub fn simple_test() {
139 let connection = init_database();
140 let mut cursor = connection
141 .fetch(GetTestEntityQuery::new(WhereCondition::default()))
142 .unwrap();
143 let entity = cursor.next().expect("there should be two results, none returned");
144 assert_eq!(
145 TestEntity {
146 text_data: "row 1".to_string(),
147 real_data: 1.23,
148 integer_data: -52,
149 maybe_null: None
150 },
151 entity
152 );
153 let entity = cursor.next().expect("there should be two results, only one returned");
154 assert_eq!(
155 TestEntity {
156 text_data: "row 2".to_string(),
157 real_data: 2.34,
158 integer_data: 1789,
159 maybe_null: Some(0)
160 },
161 entity
162 );
163
164 assert!(cursor.next().is_none(), "there should be no result");
165 }
166
167 #[test]
168 pub fn test_condition() {
169 let connection = init_database();
170 let mut cursor = connection
171 .fetch(GetTestEntityQuery::new(WhereCondition::new(
172 "maybe_null is not null",
173 Vec::new(),
174 )))
175 .unwrap();
176 let entity = cursor.next().expect("there should be one result, none returned");
177 assert_eq!(
178 TestEntity {
179 text_data: "row 2".to_string(),
180 real_data: 2.34,
181 integer_data: 1789,
182 maybe_null: Some(0)
183 },
184 entity
185 );
186 assert!(cursor.next().is_none());
187 }
188
189 #[test]
190 pub fn test_parameters() {
191 let connection = init_database();
192 let mut cursor = connection
193 .fetch(GetTestEntityQuery::new(WhereCondition::new(
194 "text_data like ?",
195 vec![Value::String("%1".to_string())],
196 )))
197 .unwrap();
198 let entity = cursor.next().expect("there should be one result, none returned");
199 assert_eq!(
200 TestEntity {
201 text_data: "row 1".to_string(),
202 real_data: 1.23,
203 integer_data: -52,
204 maybe_null: None
205 },
206 entity
207 );
208 assert!(cursor.next().is_none());
209 }
210
211 #[test]
212 fn test_upsertion() {
213 let connection = init_database();
214 let params = [
215 Value::String("row 1".to_string()),
216 Value::Float(1.234),
217 Value::Integer(0),
218 Value::Null,
219 ]
220 .to_vec();
221 let mut cursor = connection
222 .fetch(UpdateTestEntityQuery::new(WhereCondition::new("", params)))
223 .unwrap();
224
225 let entity = cursor.next().expect("there should be one result, none returned");
226 assert_eq!(
227 TestEntity {
228 text_data: "row 1".to_string(),
229 real_data: 1.234,
230 integer_data: 0,
231 maybe_null: None
232 },
233 entity
234 );
235 assert!(cursor.next().is_none());
236 }
237}