toasty_driver_integration_suite/tests/
query_in_list.rs1use crate::prelude::*;
16
17use toasty_core::{
18 driver::{Capability, Operation, operation::QuerySql},
19 schema::db,
20 stmt,
21};
22
23#[driver_test(id(ID), scenario(crate::scenarios::in_list_item))]
24pub async fn in_list_string(t: &mut Test) -> Result<()> {
25 let mut db = setup(t).await;
26
27 toasty::create!(Item::[
28 { name: "a", n: 1 },
29 { name: "b", n: 2 },
30 { name: "c", n: 3 },
31 { name: "d", n: 4 },
32 ])
33 .exec(&mut db)
34 .await?;
35
36 t.log().clear();
37
38 let items = Item::filter(Item::fields().name().in_list(["a", "c"]))
39 .exec(&mut db)
40 .await?;
41
42 assert_eq!(items.len(), 2);
43 let mut names: Vec<_> = items.iter().map(|i| i.name.clone()).collect();
44 names.sort();
45 assert_eq!(names, vec!["a".to_string(), "c".to_string()]);
46
47 if t.capability().sql {
48 let elem = column_storage_ty(&db, "items", "name");
49 assert_in_list_bind(&pop_select(t), t.capability(), &elem, Some(2));
50 }
51
52 Ok(())
53}
54
55#[driver_test(id(ID), scenario(crate::scenarios::in_list_item))]
56pub async fn not_in_list_string(t: &mut Test) -> Result<()> {
57 let mut db = setup(t).await;
58
59 toasty::create!(Item::[
60 { name: "a", n: 1 },
61 { name: "b", n: 2 },
62 { name: "c", n: 3 },
63 { name: "d", n: 4 },
64 ])
65 .exec(&mut db)
66 .await?;
67
68 t.log().clear();
69
70 let items = Item::filter(Item::fields().name().in_list(["a", "c"]).not())
72 .exec(&mut db)
73 .await?;
74
75 assert_eq!(items.len(), 2);
76 let mut names: Vec<_> = items.iter().map(|i| i.name.clone()).collect();
77 names.sort();
78 assert_eq!(names, vec!["b".to_string(), "d".to_string()]);
79
80 if t.capability().sql {
81 let elem = column_storage_ty(&db, "items", "name");
82 assert_in_list_bind(&pop_select(t), t.capability(), &elem, Some(2));
83 }
84
85 Ok(())
86}
87
88#[driver_test(id(ID), scenario(crate::scenarios::in_list_item))]
89pub async fn in_list_empty(t: &mut Test) -> Result<()> {
90 let mut db = setup(t).await;
91
92 toasty::create!(Item::[
93 { name: "a", n: 1 },
94 { name: "b", n: 2 },
95 ])
96 .exec(&mut db)
97 .await?;
98
99 t.log().clear();
100
101 let empty: Vec<String> = vec![];
107 let items = Item::filter(Item::fields().name().in_list(empty))
108 .exec(&mut db)
109 .await?;
110
111 assert!(items.is_empty(), "IN () must match nothing, got {items:?}");
112 assert!(
113 t.log().is_empty(),
114 "empty IN () should short-circuit before issuing any driver op",
115 );
116
117 Ok(())
118}
119
120#[driver_test(id(ID), scenario(crate::scenarios::in_list_item))]
121pub async fn in_list_i64_large(t: &mut Test) -> Result<()> {
122 let mut db = setup(t).await;
127
128 for n in 0..200_i64 {
129 Item::create()
130 .name(format!("item-{n}"))
131 .n(n)
132 .exec(&mut db)
133 .await?;
134 }
135
136 let needles: Vec<i64> = (0..200_i64).step_by(2).collect();
137 let expected = needles.len();
138
139 t.log().clear();
140
141 let items = Item::filter(Item::fields().n().in_list(needles))
142 .exec(&mut db)
143 .await?;
144
145 assert_eq!(items.len(), expected);
146
147 if t.capability().sql {
148 let elem = column_storage_ty(&db, "items", "n");
149 assert_in_list_bind(&pop_select(t), t.capability(), &elem, Some(expected));
150 }
151
152 Ok(())
153}
154
155#[driver_test(id(ID), scenario(crate::scenarios::in_list_item))]
156pub async fn in_list_id(t: &mut Test) -> Result<()> {
157 let mut db = setup(t).await;
162
163 let items = toasty::create!(Item::[
164 { name: "a", n: 1 },
165 { name: "b", n: 2 },
166 { name: "c", n: 3 },
167 { name: "d", n: 4 },
168 ])
169 .exec(&mut db)
170 .await?;
171
172 let needles = vec![items[0].id, items[2].id];
173
174 t.log().clear();
175
176 let found = Item::filter(Item::fields().id().in_list(needles))
177 .exec(&mut db)
178 .await?;
179
180 assert_eq!(found.len(), 2);
181 let mut names: Vec<_> = found.iter().map(|i| i.name.clone()).collect();
182 names.sort();
183 assert_eq!(names, vec!["a".to_string(), "c".to_string()]);
184
185 if t.capability().sql {
186 let elem = column_storage_ty(&db, "items", "id");
187 assert_in_list_bind(&pop_select(t), t.capability(), &elem, Some(2));
188 }
189
190 Ok(())
191}
192
193#[driver_test(id(ID), scenario(crate::scenarios::in_list_item))]
194pub async fn in_list_with_null(t: &mut Test) -> Result<()> {
195 let mut db = setup(t).await;
203
204 toasty::create!(Item::[
205 { name: "a", n: 1, bio: "rusty" },
206 { name: "b", n: 2 },
207 { name: "c", n: 3, bio: "databases" },
208 ])
209 .exec(&mut db)
210 .await?;
211
212 t.log().clear();
213
214 let needles: Vec<Option<String>> = vec![Some("rusty".to_string()), None];
215 let items = Item::filter(Item::fields().bio().in_list(needles))
216 .exec(&mut db)
217 .await?;
218
219 assert_eq!(items.len(), 1);
220 assert_eq!(items[0].name, "a");
221
222 if t.capability().sql {
223 let elem = column_storage_ty(&db, "items", "bio");
227 assert_in_list_bind(&pop_select(t), t.capability(), &elem, None);
228 }
229
230 Ok(())
231}
232
233#[track_caller]
239fn pop_select(t: &mut Test) -> QuerySql {
240 while !t.log().is_empty() {
241 if let Operation::QuerySql(q) = t.log().pop_op()
242 && matches!(q.stmt, stmt::Statement::Query(_))
243 {
244 return q;
245 }
246 }
247 panic!("expected a SELECT QuerySql op in the log");
248}
249
250fn column_storage_ty(db: &toasty::Db, table_name: &str, column_name: &str) -> db::Type {
255 let schema = db.schema();
256 let table = schema
257 .db
258 .tables
259 .iter()
260 .find(|t| t.name == table_name || t.name.ends_with(table_name))
261 .unwrap_or_else(|| panic!("table '{table_name}' not in schema"));
262 table
263 .columns
264 .iter()
265 .find(|c| c.name == column_name)
266 .unwrap_or_else(|| panic!("column '{column_name}' not in table '{table_name}'"))
267 .storage_ty
268 .clone()
269}
270
271#[track_caller]
277fn assert_in_list_bind(
278 query: &QuerySql,
279 cap: &Capability,
280 elem: &db::Type,
281 expected_scalars: Option<usize>,
282) {
283 if cap.bind_list_param && cap.predicate_match_any {
284 let expected_ty = db::Type::List(Box::new(elem.clone()));
285 assert_struct!(query, {
286 params: [{
287 value: stmt::Value::List(_),
288 ty: == expected_ty,
289 }],
290 });
291 } else {
292 if let Some(n) = expected_scalars {
293 assert_eq!(query.params.len(), n);
294 }
295 for (i, p) in query.params.iter().enumerate() {
296 assert_eq!(
297 &p.ty, elem,
298 "params[{i}].ty should be the column storage type"
299 );
300 }
301 }
302}