diesel/mysql_like/connection/stmt/
iterator.rs1#![allow(unsafe_code)] use alloc::rc::Rc;
3use core::cell::{Ref, RefCell};
4use core::marker::PhantomData;
5
6use super::{OutputBinds, Statement, StatementMetadata, StatementUse};
7use crate::backend::Backend;
8use crate::connection::statement_cache::MaybeCached;
9use crate::mysql_like::{MysqlLikeBackend, MysqlType};
10use crate::result::QueryResult;
11use crate::row::*;
12
13#[allow(missing_debug_implementations)]
14pub struct StatementIterator<'a, DB: MysqlLikeBackend> {
15 stmt: StatementUse<'a, DB>,
16 last_row: Rc<RefCell<PrivateMysqlRow>>,
17 metadata: Rc<StatementMetadata>,
18 len: usize,
19}
20
21impl<'a, DB: MysqlLikeBackend> StatementIterator<'a, DB> {
22 pub fn from_stmt(
23 stmt: MaybeCached<'a, Statement<DB>>,
24 types: &[Option<MysqlType>],
25 ) -> QueryResult<Self> {
26 let metadata = stmt.metadata()?;
27
28 let mut output_binds = OutputBinds::from_output_types(types, &metadata)
29 .map_err(crate::result::Error::DeserializationError)?;
30
31 let mut stmt = stmt.execute_statement(&mut output_binds)?;
32 let size = unsafe { stmt.result_size() }?;
33
34 Ok(StatementIterator {
35 metadata: Rc::new(metadata),
36 last_row: Rc::new(RefCell::new(PrivateMysqlRow::Direct(output_binds))),
37 len: size,
38 stmt,
39 })
40 }
41}
42
43impl<DB: MysqlLikeBackend> Iterator for StatementIterator<'_, DB> {
44 type Item = QueryResult<MysqlRow<DB>>;
45
46 fn next(&mut self) -> Option<Self::Item> {
47 let res = if let Some(binds) = Rc::get_mut(&mut self.last_row) {
52 if let PrivateMysqlRow::Direct(binds) = RefCell::get_mut(binds) {
53 self.stmt.populate_row_buffers(binds)
54 } else {
55 {
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("You\'ve reached an impossible internal state. If you ever see this error message please open an issue at https://github.com/diesel-rs/diesel providing example code how to trigger this error.")));
}unreachable!(
59 "You've reached an impossible internal state. \
60 If you ever see this error message please open \
61 an issue at https://github.com/diesel-rs/diesel \
62 providing example code how to trigger this error."
63 )
64 }
65 } else {
66 let mut last_row = {
71 let mut last_row = match self.last_row.try_borrow_mut() {
72 Ok(o) => o,
73 Err(_e) => {
74 return Some(Err(crate::result::Error::DeserializationError(
75 "Failed to reborrow row. Try to release any `MysqlField` or `MysqlValue` \
76 that exists at this point"
77 .into(),
78 )));
79 }
80 };
81 let last_row = &mut *last_row;
82 let duplicated = last_row.duplicate();
83 core::mem::replace(last_row, duplicated)
84 };
85 let res = if let PrivateMysqlRow::Direct(ref mut binds) = last_row {
86 self.stmt.populate_row_buffers(binds)
87 } else {
88 {
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("You\'ve reached an impossible internal state. If you ever see this error message please open an issue at https://github.com/diesel-rs/diesel providing example code how to trigger this error.")));
}unreachable!(
92 "You've reached an impossible internal state. \
93 If you ever see this error message please open \
94 an issue at https://github.com/diesel-rs/diesel \
95 providing example code how to trigger this error."
96 )
97 };
98 self.last_row = Rc::new(RefCell::new(last_row));
99 res
100 };
101
102 match res {
103 Ok(Some(())) => {
104 self.len = self.len.saturating_sub(1);
105 Some(Ok(MysqlRow {
106 metadata: self.metadata.clone(),
107 row: self.last_row.clone(),
108 _phantom: PhantomData,
109 }))
110 }
111 Ok(None) => None,
112 Err(e) => {
113 self.len = self.len.saturating_sub(1);
114 Some(Err(e))
115 }
116 }
117 }
118
119 fn size_hint(&self) -> (usize, Option<usize>) {
120 (self.len(), Some(self.len()))
121 }
122
123 fn count(self) -> usize
124 where
125 Self: Sized,
126 {
127 self.len()
128 }
129}
130
131impl<DB: MysqlLikeBackend> ExactSizeIterator for StatementIterator<'_, DB> {
132 fn len(&self) -> usize {
133 self.len
134 }
135}
136
137#[derive(#[automatically_derived]
#[allow(missing_debug_implementations)]
impl<DB: ::core::clone::Clone + MysqlLikeBackend> ::core::clone::Clone for
MysqlRow<DB> {
#[inline]
fn clone(&self) -> MysqlRow<DB> {
MysqlRow {
row: ::core::clone::Clone::clone(&self.row),
metadata: ::core::clone::Clone::clone(&self.metadata),
_phantom: ::core::clone::Clone::clone(&self._phantom),
}
}
}Clone)]
138#[allow(missing_debug_implementations)]
139pub struct MysqlRow<DB: MysqlLikeBackend> {
140 row: Rc<RefCell<PrivateMysqlRow>>,
141 metadata: Rc<StatementMetadata>,
142 _phantom: PhantomData<DB>,
143}
144
145enum PrivateMysqlRow {
146 Direct(OutputBinds),
147 Copied(OutputBinds),
148}
149
150impl PrivateMysqlRow {
151 fn duplicate(&self) -> Self {
152 match self {
153 Self::Copied(b) | Self::Direct(b) => Self::Copied(b.clone()),
154 }
155 }
156}
157
158impl<DB: MysqlLikeBackend> RowSealed for MysqlRow<DB> {}
159
160impl<'a, DB: MysqlLikeBackend> Row<'a, DB> for MysqlRow<DB> {
161 type Field<'f>
162 = MysqlLikeField<'f, DB>
163 where
164 'a: 'f,
165 Self: 'f;
166 type InnerPartialRow = Self;
167
168 fn field_count(&self) -> usize {
169 self.metadata.fields().len()
170 }
171
172 fn get<'b, I>(&'b self, idx: I) -> Option<Self::Field<'b>>
173 where
174 'a: 'b,
175 Self: RowIndex<I>,
176 {
177 let idx = self.idx(idx)?;
178 Some(MysqlLikeField {
179 binds: self.row.borrow(),
180 metadata: self.metadata.clone(),
181 idx,
182 _phantom: PhantomData,
183 })
184 }
185
186 fn partial_row(&self, range: core::ops::Range<usize>) -> PartialRow<'_, Self::InnerPartialRow> {
187 PartialRow::new::<DB>(self, range)
188 }
189}
190
191impl<DB: MysqlLikeBackend> RowIndex<usize> for MysqlRow<DB>
192where
193 MysqlRow<DB>: for<'a> Row<'a, DB>,
194{
195 fn idx(&self, idx: usize) -> Option<usize> {
196 if idx < self.field_count() {
197 Some(idx)
198 } else {
199 None
200 }
201 }
202}
203
204impl<'a, DB: MysqlLikeBackend> RowIndex<&'a str> for MysqlRow<DB> {
205 fn idx(&self, idx: &'a str) -> Option<usize> {
206 self.metadata
207 .fields()
208 .iter()
209 .enumerate()
210 .find(|(_, field_meta)| field_meta.field_name() == Some(idx))
211 .map(|(idx, _)| idx)
212 }
213}
214
215#[allow(missing_debug_implementations)]
216pub struct MysqlLikeField<'a, DB: MysqlLikeBackend> {
217 binds: Ref<'a, PrivateMysqlRow>,
218 metadata: Rc<StatementMetadata>,
219 idx: usize,
220 _phantom: PhantomData<DB>,
221}
222
223impl<'a, DB: MysqlLikeBackend> Field<'a, DB> for MysqlLikeField<'a, DB> {
224 fn field_name(&self) -> Option<&str> {
225 self.metadata.fields()[self.idx].field_name()
226 }
227
228 fn is_null(&self) -> bool {
229 match &*self.binds {
230 PrivateMysqlRow::Copied(b) | PrivateMysqlRow::Direct(b) => b[self.idx].is_null(),
231 }
232 }
233
234 fn value(&self) -> Option<<DB as Backend>::RawValue<'_>> {
235 match &*self.binds {
236 PrivateMysqlRow::Copied(b) | PrivateMysqlRow::Direct(b) => b[self.idx].value(),
237 }
238 }
239}
240
241#[cfg(all(test, any(feature = "mysql", feature = "mariadb")))]
242#[diesel_test_helper::test]
243#[allow(clippy::drop_non_drop)] fn fun_with_row_iters() {
245 crate::table! {
246 users(id) {
247 id -> Integer,
248 name -> Text,
249 }
250 }
251
252 use crate::connection::LoadConnection;
253 use crate::deserialize::{FromSql, FromSqlRow};
254 use crate::prelude::*;
255 use crate::row::{Field, Row};
256 use crate::sql_types;
257
258 #[cfg(feature = "mysql")]
259 type DB = crate::mysql::Mysql;
260 #[cfg(feature = "mariadb")]
261 type DB = crate::mariadb::Mariadb;
262
263 let conn = &mut crate::test_helpers::connection();
264
265 crate::sql_query(
266 "CREATE TEMPORARY TABLE IF NOT EXISTS users(id INTEGER PRIMARY KEY, name TEXT NOT NULL);",
267 )
268 .execute(conn)
269 .unwrap();
270
271 crate::insert_into(users::table)
272 .values(vec![
273 (users::id.eq(1), users::name.eq("Sean")),
274 (users::id.eq(2), users::name.eq("Tess")),
275 ])
276 .execute(conn)
277 .unwrap();
278
279 let query = users::table.select((users::id, users::name));
280
281 let expected = vec![(1, String::from("Sean")), (2, String::from("Tess"))];
282
283 {
284 let row_iter = conn.load(query).unwrap();
285 for (row, expected) in row_iter.zip(&expected) {
286 let row = row.unwrap();
287
288 let deserialized = <(i32, String) as FromSqlRow<
289 (sql_types::Integer, sql_types::Text),
290 _,
291 >>::build_from_row(&row)
292 .unwrap();
293
294 assert_eq!(&deserialized, expected);
295 }
296 }
297
298 {
299 let collected_rows = conn.load(query).unwrap().collect::<Vec<_>>();
300 assert_eq!(collected_rows.len(), 2);
301 for (row, expected) in collected_rows.iter().zip(&expected) {
302 let deserialized = row
303 .as_ref()
304 .map(|row| {
305 <(i32, String) as FromSqlRow<
306 (sql_types::Integer, sql_types::Text),
307 _,
308 >>::build_from_row(row).unwrap()
309 })
310 .unwrap();
311 assert_eq!(&deserialized, expected);
312 }
313 }
314
315 let mut row_iter = conn.load(query).unwrap();
316
317 let first_row = row_iter.next().unwrap().unwrap();
318 let first_fields = (
319 Row::get(&first_row, 0).unwrap(),
320 Row::get(&first_row, 1).unwrap(),
321 );
322 let first_values = (first_fields.0.value(), first_fields.1.value());
323
324 assert!(row_iter.next().unwrap().is_err());
325 std::mem::drop(first_values);
326 assert!(row_iter.next().unwrap().is_err());
327 std::mem::drop(first_fields);
328
329 let second_row = row_iter.next().unwrap().unwrap();
330 let second_fields = (
331 Row::get(&second_row, 0).unwrap(),
332 Row::get(&second_row, 1).unwrap(),
333 );
334 let second_values = (second_fields.0.value(), second_fields.1.value());
335
336 assert!(row_iter.next().unwrap().is_err());
337 std::mem::drop(second_values);
338 assert!(row_iter.next().unwrap().is_err());
339 std::mem::drop(second_fields);
340
341 assert!(row_iter.next().is_none());
342
343 let first_fields = (
344 Row::get(&first_row, 0).unwrap(),
345 Row::get(&first_row, 1).unwrap(),
346 );
347 let second_fields = (
348 Row::get(&second_row, 0).unwrap(),
349 Row::get(&second_row, 1).unwrap(),
350 );
351
352 let first_values = (first_fields.0.value(), first_fields.1.value());
353 let second_values = (second_fields.0.value(), second_fields.1.value());
354
355 assert_eq!(
356 <i32 as FromSql<sql_types::Integer, DB>>::from_nullable_sql(first_values.0).unwrap(),
357 expected[0].0
358 );
359 assert_eq!(
360 <String as FromSql<sql_types::Text, DB>>::from_nullable_sql(first_values.1).unwrap(),
361 expected[0].1
362 );
363
364 assert_eq!(
365 <i32 as FromSql<sql_types::Integer, DB>>::from_nullable_sql(second_values.0).unwrap(),
366 expected[1].0
367 );
368 assert_eq!(
369 <String as FromSql<sql_types::Text, DB>>::from_nullable_sql(second_values.1).unwrap(),
370 expected[1].1
371 );
372
373 let first_fields = (
374 Row::get(&first_row, 0).unwrap(),
375 Row::get(&first_row, 1).unwrap(),
376 );
377 let first_values = (first_fields.0.value(), first_fields.1.value());
378
379 assert_eq!(
380 <i32 as FromSql<sql_types::Integer, DB>>::from_nullable_sql(first_values.0).unwrap(),
381 expected[0].0
382 );
383 assert_eq!(
384 <String as FromSql<sql_types::Text, DB>>::from_nullable_sql(first_values.1).unwrap(),
385 expected[0].1
386 );
387}