Skip to main content

diesel/mysql_like/connection/stmt/
iterator.rs

1#![allow(unsafe_code)] // module uses ffi
2use 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        // check if we own the only instance of the bind buffer
48        // if that's the case we can reuse the underlying allocations
49        // if that's not the case, we need to copy the output bind buffers
50        // to somewhere else
51        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                // any other state than `PrivateMysqlRow::Direct` is invalid here
56                // and should not happen. If this ever happens this is a logic error
57                // in the code above
58                {
    ::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            // The shared bind buffer is in use by someone else,
67            // this means we copy out the values and replace the used reference
68            // by the copied values. After this we can advance the statement
69            // another step
70            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                // any other state than `PrivateMysqlRow::Direct` is invalid here
89                // and should not happen. If this ever happens this is a logic error
90                // in the code above
91                {
    ::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)] // we want to explicitly extend lifetimes here
244fn 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}