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::Error::DeserializationError;
11use crate::result::QueryResult;
12use crate::row::*;
13
14#[allow(missing_debug_implementations)]
15pub struct StatementIterator<'a, DB: MysqlLikeBackend> {
16    stmt: StatementUse<'a, DB>,
17    last_row: Rc<RefCell<PrivateMysqlRow>>,
18    metadata: Rc<StatementMetadata>,
19    len: usize,
20}
21
22impl<'a, DB: MysqlLikeBackend> StatementIterator<'a, DB> {
23    pub fn from_stmt(
24        stmt: MaybeCached<'a, Statement<DB>>,
25        types: &[Option<MysqlType>],
26    ) -> QueryResult<Self> {
27        let mut stmt = if let Some(metadata) = stmt.metadata()? {
28            let mut output_binds =
29                OutputBinds::from_output_types(types, &metadata).map_err(DeserializationError)?;
30            /*
31                This may seem redundant but if we don't do this we will hit
32                a memory bug in `libmysqlclient`
33            */
34            stmt.execute_statement(&mut output_binds)?
35        } else {
36            //Sometimes we must execute the statement to get its metadata.
37            unsafe { stmt.execute()? }
38        };
39
40        let metadata = stmt.metadata()?;
41        let output_binds =
42            OutputBinds::from_output_types(types, &metadata).map_err(DeserializationError)?;
43
44        let size = unsafe { stmt.result_size() }?;
45
46        Ok(StatementIterator {
47            metadata: Rc::new(metadata),
48            last_row: Rc::new(RefCell::new(PrivateMysqlRow::Direct(output_binds))),
49            len: size,
50            stmt,
51        })
52    }
53}
54
55impl<DB: MysqlLikeBackend> Iterator for StatementIterator<'_, DB> {
56    type Item = QueryResult<MysqlRow<DB>>;
57
58    fn next(&mut self) -> Option<Self::Item> {
59        // check if we own the only instance of the bind buffer
60        // if that's the case we can reuse the underlying allocations
61        // if that's not the case, we need to copy the output bind buffers
62        // to somewhere else
63        let res = if let Some(binds) = Rc::get_mut(&mut self.last_row) {
64            if let PrivateMysqlRow::Direct(binds) = RefCell::get_mut(binds) {
65                self.stmt.populate_row_buffers(binds)
66            } else {
67                // any other state than `PrivateMysqlRow::Direct` is invalid here
68                // and should not happen. If this ever happens this is a logic error
69                // in the code above
70                {
    ::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!(
71                    "You've reached an impossible internal state. \
72                     If you ever see this error message please open \
73                     an issue at https://github.com/diesel-rs/diesel \
74                     providing example code how to trigger this error."
75                )
76            }
77        } else {
78            // The shared bind buffer is in use by someone else,
79            // this means we copy out the values and replace the used reference
80            // by the copied values. After this we can advance the statement
81            // another step
82            let mut last_row = {
83                let mut last_row = match self.last_row.try_borrow_mut() {
84                    Ok(o) => o,
85                    Err(_e) => {
86                        return Some(Err(DeserializationError(
87                            "Failed to reborrow row. Try to release any `MysqlField` or `MysqlValue` \
88                             that exists at this point"
89                                .into(),
90                        )));
91                    }
92                };
93                let last_row = &mut *last_row;
94                let duplicated = last_row.duplicate();
95                core::mem::replace(last_row, duplicated)
96            };
97            let res = if let PrivateMysqlRow::Direct(ref mut binds) = last_row {
98                self.stmt.populate_row_buffers(binds)
99            } else {
100                // any other state than `PrivateMysqlRow::Direct` is invalid here
101                // and should not happen. If this ever happens this is a logic error
102                // in the code above
103                {
    ::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!(
104                    "You've reached an impossible internal state. \
105                     If you ever see this error message please open \
106                     an issue at https://github.com/diesel-rs/diesel \
107                     providing example code how to trigger this error."
108                )
109            };
110            self.last_row = Rc::new(RefCell::new(last_row));
111            res
112        };
113
114        match res {
115            Ok(Some(())) => {
116                self.len = self.len.saturating_sub(1);
117                Some(Ok(MysqlRow {
118                    metadata: self.metadata.clone(),
119                    row: self.last_row.clone(),
120                    _phantom: PhantomData,
121                }))
122            }
123            Ok(None) => None,
124            Err(e) => {
125                self.len = self.len.saturating_sub(1);
126                Some(Err(e))
127            }
128        }
129    }
130
131    fn size_hint(&self) -> (usize, Option<usize>) {
132        (self.len(), Some(self.len()))
133    }
134
135    fn count(self) -> usize
136    where
137        Self: Sized,
138    {
139        self.len()
140    }
141}
142
143impl<DB: MysqlLikeBackend> ExactSizeIterator for StatementIterator<'_, DB> {
144    fn len(&self) -> usize {
145        self.len
146    }
147}
148
149#[derive(#[automatically_derived]#[allow(missing_debug_implementations)]
impl<DB: ::core::clone::Clone + MysqlLikeBackend> ::core::clone::Clone for
    MysqlRow<DB> {
    #[inline]
    #[rustc_trivial_field_reads]
    fn clone(&self) -> Self {
        MysqlRow {
            row: ::core::clone::Clone::clone(&self.row),
            metadata: ::core::clone::Clone::clone(&self.metadata),
            _phantom: ::core::clone::Clone::clone(&self._phantom),
        }
    }
}Clone)]
150#[allow(missing_debug_implementations)]
151pub struct MysqlRow<DB: MysqlLikeBackend> {
152    row: Rc<RefCell<PrivateMysqlRow>>,
153    metadata: Rc<StatementMetadata>,
154    _phantom: PhantomData<DB>,
155}
156
157enum PrivateMysqlRow {
158    Direct(OutputBinds),
159    Copied(OutputBinds),
160}
161
162impl PrivateMysqlRow {
163    fn duplicate(&self) -> Self {
164        match self {
165            Self::Copied(b) | Self::Direct(b) => Self::Copied(b.clone()),
166        }
167    }
168}
169
170impl<DB: MysqlLikeBackend> RowSealed for MysqlRow<DB> {}
171
172impl<'a, DB: MysqlLikeBackend> Row<'a, DB> for MysqlRow<DB> {
173    type Field<'f>
174        = MysqlLikeField<'f, DB>
175    where
176        'a: 'f,
177        Self: 'f;
178    type InnerPartialRow = Self;
179
180    fn field_count(&self) -> usize {
181        self.metadata.fields().len()
182    }
183
184    fn get<'b, I>(&'b self, idx: I) -> Option<Self::Field<'b>>
185    where
186        'a: 'b,
187        Self: RowIndex<I>,
188    {
189        let idx = self.idx(idx)?;
190        Some(MysqlLikeField {
191            binds: self.row.borrow(),
192            metadata: self.metadata.clone(),
193            idx,
194            _phantom: PhantomData,
195        })
196    }
197
198    fn partial_row(&self, range: core::ops::Range<usize>) -> PartialRow<'_, Self::InnerPartialRow> {
199        PartialRow::new::<DB>(self, range)
200    }
201}
202
203impl<DB: MysqlLikeBackend> RowIndex<usize> for MysqlRow<DB>
204where
205    MysqlRow<DB>: for<'a> Row<'a, DB>,
206{
207    fn idx(&self, idx: usize) -> Option<usize> {
208        if idx < self.field_count() {
209            Some(idx)
210        } else {
211            None
212        }
213    }
214}
215
216impl<'a, DB: MysqlLikeBackend> RowIndex<&'a str> for MysqlRow<DB> {
217    fn idx(&self, idx: &'a str) -> Option<usize> {
218        self.metadata
219            .fields()
220            .iter()
221            .enumerate()
222            .find(|(_, field_meta)| field_meta.field_name() == Some(idx))
223            .map(|(idx, _)| idx)
224    }
225}
226
227#[allow(missing_debug_implementations)]
228pub struct MysqlLikeField<'a, DB: MysqlLikeBackend> {
229    binds: Ref<'a, PrivateMysqlRow>,
230    metadata: Rc<StatementMetadata>,
231    idx: usize,
232    _phantom: PhantomData<DB>,
233}
234
235impl<'a, DB: MysqlLikeBackend> Field<'a, DB> for MysqlLikeField<'a, DB> {
236    fn field_name(&self) -> Option<&str> {
237        self.metadata.fields()[self.idx].field_name()
238    }
239
240    fn is_null(&self) -> bool {
241        match &*self.binds {
242            PrivateMysqlRow::Copied(b) | PrivateMysqlRow::Direct(b) => b[self.idx].is_null(),
243        }
244    }
245
246    fn value(&self) -> Option<<DB as Backend>::RawValue<'_>> {
247        match &*self.binds {
248            PrivateMysqlRow::Copied(b) | PrivateMysqlRow::Direct(b) => b[self.idx].value(),
249        }
250    }
251}
252
253#[cfg(test)]
254mod tests {
255
256    use crate::connection::LoadConnection;
257    use crate::deserialize::{FromSql, FromSqlRow};
258    use crate::prelude::*;
259    use crate::row::{Field, Row};
260    use crate::sql_types;
261
262    #[diesel_test_helper::test]
263    #[allow(clippy::drop_non_drop)] // we want to explicitly extend lifetimes here
264    fn fun_with_row_iters() {
265        crate::table! {
266            users(id) {
267                id -> Integer,
268                name -> Text,
269            }
270        }
271
272        #[cfg(feature = "mysql")]
273        type DB = crate::mysql::Mysql;
274        #[cfg(feature = "mariadb")]
275        type DB = crate::mariadb::Mariadb;
276
277        let conn = &mut crate::test_helpers::connection();
278
279        crate::sql_query(
280            "CREATE TEMPORARY TABLE IF NOT EXISTS users(id INTEGER PRIMARY KEY, name TEXT NOT NULL);",
281        )
282        .execute(conn)
283        .unwrap();
284
285        crate::insert_into(users::table)
286            .values(vec![
287                (users::id.eq(1), users::name.eq("Sean")),
288                (users::id.eq(2), users::name.eq("Tess")),
289            ])
290            .execute(conn)
291            .unwrap();
292
293        let query = users::table.select((users::id, users::name));
294
295        let expected = vec![(1, String::from("Sean")), (2, String::from("Tess"))];
296
297        {
298            let row_iter = conn.load(query).unwrap();
299            for (row, expected) in row_iter.zip(&expected) {
300                let row = row.unwrap();
301
302                let deserialized = <(i32, String) as FromSqlRow<
303                    (sql_types::Integer, sql_types::Text),
304                    _,
305                >>::build_from_row(&row)
306                .unwrap();
307
308                assert_eq!(&deserialized, expected);
309            }
310        }
311
312        {
313            let collected_rows = conn.load(query).unwrap().collect::<Vec<_>>();
314            assert_eq!(collected_rows.len(), 2);
315            for (row, expected) in collected_rows.iter().zip(&expected) {
316                let deserialized = row
317                    .as_ref()
318                    .map(|row| {
319                        <(i32, String) as FromSqlRow<
320                            (sql_types::Integer, sql_types::Text),
321                        _,
322                        >>::build_from_row(row).unwrap()
323                    })
324                    .unwrap();
325                assert_eq!(&deserialized, expected);
326            }
327        }
328
329        let mut row_iter = conn.load(query).unwrap();
330
331        let first_row = row_iter.next().unwrap().unwrap();
332        let first_fields = (
333            Row::get(&first_row, 0).unwrap(),
334            Row::get(&first_row, 1).unwrap(),
335        );
336        let first_values = (first_fields.0.value(), first_fields.1.value());
337
338        assert!(row_iter.next().unwrap().is_err());
339        std::mem::drop(first_values);
340        assert!(row_iter.next().unwrap().is_err());
341        std::mem::drop(first_fields);
342
343        let second_row = row_iter.next().unwrap().unwrap();
344        let second_fields = (
345            Row::get(&second_row, 0).unwrap(),
346            Row::get(&second_row, 1).unwrap(),
347        );
348        let second_values = (second_fields.0.value(), second_fields.1.value());
349
350        assert!(row_iter.next().unwrap().is_err());
351        std::mem::drop(second_values);
352        assert!(row_iter.next().unwrap().is_err());
353        std::mem::drop(second_fields);
354
355        assert!(row_iter.next().is_none());
356
357        let first_fields = (
358            Row::get(&first_row, 0).unwrap(),
359            Row::get(&first_row, 1).unwrap(),
360        );
361        let second_fields = (
362            Row::get(&second_row, 0).unwrap(),
363            Row::get(&second_row, 1).unwrap(),
364        );
365
366        let first_values = (first_fields.0.value(), first_fields.1.value());
367        let second_values = (second_fields.0.value(), second_fields.1.value());
368
369        assert_eq!(
370            <i32 as FromSql<sql_types::Integer, DB>>::from_nullable_sql(first_values.0).unwrap(),
371            expected[0].0
372        );
373        assert_eq!(
374            <String as FromSql<sql_types::Text, DB>>::from_nullable_sql(first_values.1).unwrap(),
375            expected[0].1
376        );
377
378        assert_eq!(
379            <i32 as FromSql<sql_types::Integer, DB>>::from_nullable_sql(second_values.0).unwrap(),
380            expected[1].0
381        );
382        assert_eq!(
383            <String as FromSql<sql_types::Text, DB>>::from_nullable_sql(second_values.1).unwrap(),
384            expected[1].1
385        );
386
387        let first_fields = (
388            Row::get(&first_row, 0).unwrap(),
389            Row::get(&first_row, 1).unwrap(),
390        );
391        let first_values = (first_fields.0.value(), first_fields.1.value());
392
393        assert_eq!(
394            <i32 as FromSql<sql_types::Integer, DB>>::from_nullable_sql(first_values.0).unwrap(),
395            expected[0].0
396        );
397        assert_eq!(
398            <String as FromSql<sql_types::Text, DB>>::from_nullable_sql(first_values.1).unwrap(),
399            expected[0].1
400        );
401    }
402
403    #[diesel_test_helper::test]
404    fn output_binds_are_rebound_after_column_error() {
405        use crate::dsl::sql;
406        use crate::mysql_like::connection::DefaultLoadingMode;
407        use crate::sql_types::{Integer, Text};
408
409        let conn = &mut crate::test_helpers::connection();
410
411        diesel::sql_query(
412            "CREATE TEMPORARY TABLE f02 (id INTEGER PRIMARY KEY, txt TEXT NOT NULL, num VARCHAR(32) NOT NULL)",
413        )
414        .execute(conn)
415        .unwrap();
416
417        diesel::sql_query(
418            "INSERT INTO f02 VALUES \
419             (1, REPEAT('a', 4096), '1'), \
420             (2, REPEAT('b', 8192), '99999999999'), \
421             (3, REPEAT('c', 4096), '3')",
422        )
423        .execute(conn)
424        .unwrap();
425
426        let mut iter = sql::<(Text, Integer)>("SELECT txt, num FROM f02 ORDER BY id")
427            .load_iter::<(String, i32), DefaultLoadingMode>(conn)
428            .unwrap();
429
430        let first = iter.next().unwrap().unwrap();
431        assert_eq!(first, ("a".repeat(4096), 1));
432
433        // the overflowing column is reported as an error
434        assert!(iter.next().unwrap().is_err());
435
436        // keep iterating: this must neither touch freed memory nor return stale data
437        let third = iter.next().unwrap().unwrap();
438        assert_eq!(third, ("c".repeat(4096), 3));
439        assert!(iter.next().is_none());
440    }
441
442    #[test]
443    fn row_field_names_after_statement_close() {
444        let conn = &mut crate::test_helpers::connection();
445        conn.set_prepared_statement_cache_size(crate::connection::CacheSize::Disabled);
446        let rows: Vec<_> = conn
447            .load(diesel::sql_query("SELECT 1 AS some_column_name"))
448            .unwrap()
449            .collect::<Result<_, _>>()
450            .unwrap();
451        // the iterator, and with it the uncached MYSQL_STMT, is gone now
452        let f = Row::get(&rows[0], 0usize).unwrap();
453        assert_eq!(f.field_name(), Some("some_column_name"));
454        assert!(Row::get(&rows[0], "some_column_name").is_some());
455    }
456
457    #[test]
458    fn row_field_names_after_connection_drop() {
459        let rows: Vec<_> = {
460            let conn = &mut crate::test_helpers::connection();
461            conn.load(diesel::sql_query("SELECT 1 AS some_column_name"))
462                .unwrap()
463                .collect::<Result<_, _>>()
464                .unwrap()
465        };
466        let f = Row::get(&rows[0], 0usize).unwrap();
467        assert_eq!(f.field_name(), Some("some_column_name"));
468    }
469}