Skip to main content

toasty_driver_integration_suite/tests/
select_projection.rs

1use crate::prelude::*;
2
3/// `.select(field)` on a `Query<List<Item>>` returns a `Query<List<String>>`
4/// whose `.exec()` produces a `Vec<String>` of the projected column.
5#[driver_test(scenario(crate::scenarios::fixed_item_name_quantity))]
6pub async fn select_single_field(test: &mut Test) -> Result<()> {
7    let mut db = setup(test).await;
8
9    toasty::create!(Item::[
10        { id: 1_i64, name: "Alice",   quantity: 7_i64  },
11        { id: 2_i64, name: "Bob",     quantity: 3_i64  },
12        { id: 3_i64, name: "Charlie", quantity: 11_i64 },
13    ])
14    .exec(&mut db)
15    .await
16    .unwrap();
17
18    let mut names: Vec<String> = Item::all()
19        .select(Item::fields().name())
20        .exec(&mut db)
21        .await?;
22
23    names.sort();
24
25    assert_eq!(
26        names,
27        vec![
28            "Alice".to_string(),
29            "Bob".to_string(),
30            "Charlie".to_string()
31        ]
32    );
33
34    Ok(())
35}
36
37/// `.select((f1, f2))` returns a `Query<List<(T1, T2)>>` whose `.exec()`
38/// produces a `Vec` of tuples.
39#[driver_test(scenario(crate::scenarios::fixed_item_name_quantity))]
40pub async fn select_tuple(test: &mut Test) -> Result<()> {
41    let mut db = setup(test).await;
42
43    toasty::create!(Item::[
44        { id: 1_i64, name: "Alice",   quantity: 7_i64  },
45        { id: 2_i64, name: "Bob",     quantity: 3_i64  },
46        { id: 3_i64, name: "Charlie", quantity: 11_i64 },
47    ])
48    .exec(&mut db)
49    .await
50    .unwrap();
51
52    let mut pairs: Vec<(i64, String)> = Item::all()
53        .select((Item::fields().id(), Item::fields().name()))
54        .exec(&mut db)
55        .await?;
56
57    pairs.sort_by_key(|(id, _)| *id);
58
59    assert_eq!(
60        pairs,
61        vec![
62            (1_i64, "Alice".to_string()),
63            (2_i64, "Bob".to_string()),
64            (3_i64, "Charlie".to_string()),
65        ]
66    );
67
68    Ok(())
69}
70
71/// `.select(...)` composes with `.filter(...)`: the projection sees only rows
72/// matching the filter expression.
73#[driver_test(scenario(crate::scenarios::fixed_item_name_quantity))]
74pub async fn select_with_filter(test: &mut Test) -> Result<()> {
75    let mut db = setup(test).await;
76
77    toasty::create!(Item::[
78        { id: 1_i64, name: "Alice",   quantity: 7_i64  },
79        { id: 2_i64, name: "Bob",     quantity: 3_i64  },
80        { id: 3_i64, name: "Charlie", quantity: 11_i64 },
81    ])
82    .exec(&mut db)
83    .await
84    .unwrap();
85
86    let mut names: Vec<String> = Item::filter(Item::fields().quantity().gt(5_i64))
87        .select(Item::fields().name())
88        .exec(&mut db)
89        .await?;
90
91    names.sort();
92
93    assert_eq!(names, vec!["Alice".to_string(), "Charlie".to_string()]);
94
95    Ok(())
96}
97
98/// `.select(...).first()` lifts the outer container to `Option<T>`.
99#[driver_test(scenario(crate::scenarios::fixed_item_name_quantity))]
100pub async fn select_first(test: &mut Test) -> Result<()> {
101    let mut db = setup(test).await;
102
103    toasty::create!(Item::[
104        { id: 1_i64, name: "Alice",   quantity: 7_i64  },
105        { id: 2_i64, name: "Bob",     quantity: 3_i64  },
106        { id: 3_i64, name: "Charlie", quantity: 11_i64 },
107    ])
108    .exec(&mut db)
109    .await
110    .unwrap();
111
112    let name: Option<String> = Item::filter(Item::fields().id().eq(2_i64))
113        .select(Item::fields().name())
114        .first()
115        .exec(&mut db)
116        .await?;
117
118    assert_eq!(name.as_deref(), Some("Bob"));
119
120    Ok(())
121}
122
123/// A fully constant projection is planned as a `Repeat` over the data load.
124/// The repeated output must forward the data load's page cursors so the page
125/// can still advance.
126#[driver_test(scenario(crate::scenarios::fixed_item_name_quantity), requires(sql))]
127pub async fn select_const_preserves_pagination(test: &mut Test) -> Result<()> {
128    use toasty::stmt::{Page, Paginate};
129
130    let mut db = setup(test).await;
131
132    toasty::create!(Item::[
133        { id: 1_i64, name: "Alice",   quantity: 7_i64  },
134        { id: 2_i64, name: "Bob",     quantity: 3_i64  },
135        { id: 3_i64, name: "Charlie", quantity: 11_i64 },
136    ])
137    .exec(&mut db)
138    .await
139    .unwrap();
140
141    let query = Item::all()
142        .order_by(Item::fields().id().asc())
143        .select::<i64, i64>(1_i64);
144    let first: Page<i64> = Paginate::new(query, 2).exec(&mut db).await?;
145
146    assert_eq!(first.items, [1, 1]);
147    assert!(first.has_next());
148
149    let second = first.next(&mut db).await?.unwrap();
150    assert_eq!(second.items, [1]);
151    assert!(!second.has_next());
152
153    Ok(())
154}
155
156/// `.select(...).first()` returns `None` when no rows match.
157#[driver_test(scenario(crate::scenarios::fixed_item_name_quantity))]
158pub async fn select_first_no_match(test: &mut Test) -> Result<()> {
159    let mut db = setup(test).await;
160
161    toasty::create!(Item::[
162        { id: 1_i64, name: "Alice",   quantity: 7_i64  },
163        { id: 2_i64, name: "Bob",     quantity: 3_i64  },
164        { id: 3_i64, name: "Charlie", quantity: 11_i64 },
165    ])
166    .exec(&mut db)
167    .await
168    .unwrap();
169
170    let name: Option<String> = Item::filter(Item::fields().id().eq(999_i64))
171        .select(Item::fields().name())
172        .first()
173        .exec(&mut db)
174        .await?;
175
176    assert_eq!(name, None);
177
178    Ok(())
179}