toasty_driver_integration_suite/tests/
paginate_multi_column.rs1use crate::prelude::*;
9use toasty::{
10 Executor,
11 stmt::{IntoStatement, Page},
12};
13use toasty_core::stmt::{self, Value};
14
15fn cursor_len(cursor: Option<&Value>) -> usize {
16 let Some(Value::Record(cursor)) = cursor else {
17 panic!("expected a record cursor");
18 };
19 cursor.fields.len()
20}
21
22#[driver_test(requires(and(sql, backward_pagination)))]
23pub async fn paginate_first_page_has_no_previous_page(test: &mut Test) -> Result<()> {
24 #[derive(Debug, toasty::Model)]
25 struct Item {
26 #[key]
27 id: i64,
28 }
29
30 let mut db = test.setup_db(models!(Item)).await;
31 toasty::create!(Item::[{ id: 1 }, { id: 2 }])
32 .exec(&mut db)
33 .await?;
34
35 let first: Page<Item> = Item::all()
36 .order_by(Item::fields().id().asc())
37 .paginate(1)
38 .exec(&mut db)
39 .await?;
40
41 assert!(!first.has_prev());
42 assert!(first.prev(&mut db).await?.is_none());
43
44 Ok(())
45}
46
47#[driver_test(requires(and(sql, backward_pagination, cte_unreferenced)))]
51pub async fn paginate_first_page_ignores_subquery_cursor(test: &mut Test) -> Result<()> {
52 #[derive(Debug, toasty::Model)]
53 struct Item {
54 #[key]
55 id: i64,
56 }
57
58 let mut db = test.setup_db(models!(Item)).await;
59 toasty::create!(Item::[{ id: 1 }, { id: 2 }])
60 .exec(&mut db)
61 .await?;
62
63 let mut inner = Item::all()
64 .order_by(Item::fields().id().asc())
65 .select((Item::fields().id(), Item::fields().id()))
66 .into_statement()
67 .into_untyped()
68 .into_query_unwrap();
69 inner.limit = Some(stmt::Limit::Cursor(stmt::LimitCursor {
70 page_size: stmt::Value::from(2_i64).into(),
71 after: Some(stmt::Value::from(0_i64).into()),
72 }));
73
74 let mut statement = Item::all()
75 .order_by(Item::fields().id().asc())
76 .into_statement()
77 .into_untyped();
78
79 let outer = statement.as_query_mut().unwrap();
80 outer.limit = Some(stmt::Limit::Cursor(stmt::LimitCursor {
81 page_size: stmt::Value::from(1_i64).into(),
82 after: None,
83 }));
84 outer.with = Some(stmt::With {
85 ctes: vec![stmt::Cte { query: inner }],
86 });
87
88 let response = db.exec_untyped(statement).await?;
89 assert!(response.prev_cursor.is_none());
90
91 Ok(())
92}
93
94#[driver_test(requires(and(sql, backward_pagination)))]
95pub async fn paginate_first_page_ignores_nested_statement_cursor(test: &mut Test) -> Result<()> {
96 #[derive(Debug, toasty::Model)]
97 struct Item {
98 #[key]
99 id: i64,
100 }
101
102 let mut db = test.setup_db(models!(Item)).await;
103 toasty::create!(Item::[{ id: 1 }, { id: 2 }])
104 .exec(&mut db)
105 .await?;
106
107 let mut nested = Item::all()
108 .order_by(Item::fields().id().asc())
109 .into_statement()
110 .into_untyped();
111 nested.as_query_mut().unwrap().limit = Some(stmt::Limit::Cursor(stmt::LimitCursor {
112 page_size: stmt::Value::from(1_i64).into(),
113 after: Some(stmt::Value::from(0_i64).into()),
114 }));
115
116 let mut statement = Item::all()
117 .order_by(Item::fields().id().asc())
118 .select((Item::fields().id(), Item::fields().id()))
119 .into_statement()
120 .into_untyped();
121 let outer = statement.as_query_mut().unwrap();
122 outer.limit = Some(stmt::Limit::Cursor(stmt::LimitCursor {
123 page_size: stmt::Value::from(1_i64).into(),
124 after: None,
125 }));
126
127 let stmt::Expr::Record(returning) = outer.returning_mut_unwrap().as_project_mut_unwrap() else {
128 panic!("expected a record projection");
129 };
130 returning.fields[1] = stmt::Expr::stmt(nested);
131
132 let response = db.exec_untyped(statement).await?;
133 assert!(response.prev_cursor.is_none());
134
135 Ok(())
136}
137
138#[driver_test(requires(sql))]
139pub async fn paginate_single_non_unique_column(test: &mut Test) -> Result<()> {
140 #[derive(Debug, toasty::Model)]
141 struct Item {
142 #[key]
143 id: i64,
144 group: i64,
145 }
146
147 let mut db = test.setup_db(models!(Item)).await;
148 toasty::create!(Item::[
149 { id: 1, group: 1 },
150 { id: 2, group: 1 },
151 { id: 3, group: 1 },
152 { id: 4, group: 1 },
153 ])
154 .exec(&mut db)
155 .await?;
156
157 let first: Page<Item> = Item::all()
158 .order_by(Item::fields().group().asc())
159 .paginate(2)
160 .exec(&mut db)
161 .await?;
162 assert_struct!(first.items, [{ id: 1 }, { id: 2 }]);
163 assert_eq!(cursor_len(first.next_cursor.as_ref()), 2);
164
165 let second = first.next(&mut db).await?.unwrap();
166 assert_struct!(second.items, [{ id: 3 }, { id: 4 }]);
167
168 let first_again = second.prev(&mut db).await?.unwrap();
169 assert_struct!(first_again.items, [{ id: 1 }, { id: 2 }]);
170
171 Ok(())
172}
173
174#[driver_test(requires(sql))]
175pub async fn paginate_non_unique_column_with_newtype_primary_key(test: &mut Test) -> Result<()> {
176 #[derive(Debug, toasty::Embed)]
177 struct ItemId(i64);
178
179 #[derive(Debug, toasty::Model)]
180 struct Item {
181 #[key]
182 id: ItemId,
183 group: i64,
184 }
185
186 let mut db = test.setup_db(models!(Item)).await;
187 toasty::create!(Item::[
188 { id: ItemId(1), group: 1 },
189 { id: ItemId(2), group: 1 },
190 { id: ItemId(3), group: 1 },
191 ])
192 .exec(&mut db)
193 .await?;
194
195 let first: Page<Item> = Item::all()
196 .order_by(Item::fields().group().asc())
197 .paginate(2)
198 .exec(&mut db)
199 .await?;
200 assert_eq!(
201 first.items.iter().map(|item| item.id.0).collect::<Vec<_>>(),
202 [1, 2]
203 );
204 assert_eq!(cursor_len(first.next_cursor.as_ref()), 2);
205
206 let second = first.next(&mut db).await?.unwrap();
207 assert_eq!(
208 second
209 .items
210 .iter()
211 .map(|item| item.id.0)
212 .collect::<Vec<_>>(),
213 [3]
214 );
215
216 Ok(())
217}
218
219#[driver_test(requires(sql))]
220pub async fn paginate_accepts_cursor_without_hidden_primary_key(test: &mut Test) -> Result<()> {
221 #[derive(Debug, toasty::Model)]
222 struct Item {
223 #[key]
224 id: i64,
225 group: i64,
226 }
227
228 let mut db = test.setup_db(models!(Item)).await;
229 toasty::create!(Item::[
230 { id: 1, group: 1 },
231 { id: 2, group: 1 },
232 { id: 3, group: 1 },
233 ])
234 .exec(&mut db)
235 .await?;
236
237 let first: Page<Item> = Item::all()
238 .order_by(Item::fields().group().asc())
239 .paginate(2)
240 .after(0_i64)
241 .exec(&mut db)
242 .await?;
243 assert_struct!(first.items, [{ id: 1 }, { id: 2 }]);
244 assert_eq!(cursor_len(first.next_cursor.as_ref()), 2);
245
246 let second = first.next(&mut db).await?.unwrap();
247 assert_struct!(second.items, [{ id: 3 }]);
248
249 Ok(())
250}
251
252#[driver_test(requires(sql))]
253pub async fn paginate_unique_non_nullable_column_needs_no_tiebreaker(
254 test: &mut Test,
255) -> Result<()> {
256 #[derive(Debug, toasty::Model)]
257 struct Item {
258 #[key]
259 id: i64,
260 #[unique]
261 rank: i64,
262 }
263
264 let mut db = test.setup_db(models!(Item)).await;
265 toasty::create!(Item::[
266 { id: 1, rank: 10 },
267 { id: 2, rank: 20 },
268 { id: 3, rank: 30 },
269 ])
270 .exec(&mut db)
271 .await?;
272
273 let page: Page<Item> = Item::all()
274 .order_by(Item::fields().rank().asc())
275 .paginate(2)
276 .exec(&mut db)
277 .await?;
278 assert_eq!(cursor_len(page.next_cursor.as_ref()), 1);
279
280 Ok(())
281}
282
283#[driver_test(requires(sql))]
284pub async fn paginate_composite_unique_non_nullable_order_needs_no_tiebreaker(
285 test: &mut Test,
286) -> Result<()> {
287 #[derive(Debug, toasty::Model)]
288 #[unique(group, rank)]
289 struct Item {
290 #[key]
291 id: i64,
292 group: i64,
293 rank: i64,
294 }
295
296 let mut db = test.setup_db(models!(Item)).await;
297 toasty::create!(Item::[
298 { id: 1, group: 1, rank: 10 },
299 { id: 2, group: 1, rank: 20 },
300 { id: 3, group: 2, rank: 10 },
301 ])
302 .exec(&mut db)
303 .await?;
304
305 let page: Page<Item> = Item::all()
306 .order_by((Item::fields().group().asc(), Item::fields().rank().asc()))
307 .paginate(2)
308 .exec(&mut db)
309 .await?;
310 assert_eq!(cursor_len(page.next_cursor.as_ref()), 2);
311
312 Ok(())
313}
314
315#[driver_test(requires(sql))]
316pub async fn paginate_nullable_unique_column_uses_primary_key(test: &mut Test) -> Result<()> {
317 #[derive(Debug, toasty::Model)]
318 struct Item {
319 #[key]
320 id: i64,
321 #[unique]
322 rank: Option<i64>,
323 }
324
325 let mut db = test.setup_db(models!(Item)).await;
326 toasty::create!(Item::[
327 { id: 1, rank: None },
328 { id: 2, rank: None },
329 { id: 3, rank: Some(10) },
330 { id: 4, rank: Some(20) },
331 ])
332 .exec(&mut db)
333 .await?;
334
335 let mut page: Page<Item> = Item::all()
336 .order_by(Item::fields().rank().asc())
337 .paginate(1)
338 .exec(&mut db)
339 .await?;
340 assert_eq!(cursor_len(page.next_cursor.as_ref()), 2);
341
342 let mut ids = Vec::new();
343 loop {
344 ids.extend(page.iter().map(|item| item.id));
345 let Some(next) = page.next(&mut db).await? else {
346 break;
347 };
348 page = next;
349 }
350 ids.sort_unstable();
351 assert_eq!(ids, [1, 2, 3, 4]);
352
353 Ok(())
354}
355
356#[driver_test(requires(sql))]
357pub async fn paginate_multi_column_equal_leading_values(test: &mut Test) -> Result<()> {
358 #[derive(Debug, toasty::Model)]
359 struct Item {
360 #[key]
361 id: i64,
362 group: i64,
363 }
364
365 let mut db = test.setup_db(models!(Item)).await;
366
367 toasty::create!(Item::[
369 { id: 1, group: 1 },
370 { id: 2, group: 1 },
371 { id: 3, group: 1 },
372 ])
373 .exec(&mut db)
374 .await?;
375
376 let page: Page<Item> = Item::all()
377 .order_by((Item::fields().group().asc(), Item::fields().id().asc()))
378 .paginate(2)
379 .exec(&mut db)
380 .await?;
381 assert_struct!(page.items, [{ id: 1 }, { id: 2 }]);
382
383 let page: Page<Item> = page.next(&mut db).await?.unwrap();
384 assert_struct!(page.items, [{ id: 3 }]);
385
386 Ok(())
387}
388
389#[driver_test(requires(sql))]
390pub async fn paginate_multi_column_mixed_directions(test: &mut Test) -> Result<()> {
391 #[derive(Debug, toasty::Model)]
392 struct Item {
393 #[key]
394 id: i64,
395 group: i64,
396 }
397
398 let mut db = test.setup_db(models!(Item)).await;
399
400 toasty::create!(Item::[
401 { id: 1, group: 1 },
402 { id: 2, group: 1 },
403 { id: 3, group: 2 },
404 { id: 4, group: 2 },
405 ])
406 .exec(&mut db)
407 .await?;
408
409 let page: Page<Item> = Item::all()
411 .order_by((Item::fields().group().desc(), Item::fields().id().asc()))
412 .paginate(2)
413 .exec(&mut db)
414 .await?;
415 assert_struct!(page.items, [{ id: 3 }, { id: 4 }]);
416
417 let page: Page<Item> = page.next(&mut db).await?.unwrap();
418 assert_struct!(page.items, [{ id: 1 }, { id: 2 }]);
419
420 Ok(())
421}