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::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 stmt.execute_statement(&mut output_binds)?
35 } else {
36 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 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 {
::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 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 {
::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)] 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 assert!(iter.next().unwrap().is_err());
435
436 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 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}