1 use std::collections::HashMap;
2 
3 use super::{ffi, libc, Binds, Statement, StatementMetadata};
4 use mysql::{Mysql, MysqlType};
5 use result::QueryResult;
6 use row::*;
7 use sql_types::IsSigned;
8 
9 pub struct StatementIterator<'a> {
10     stmt: &'a mut Statement,
11     output_binds: Binds,
12 }
13 
14 #[allow(clippy::should_implement_trait)] // don't neet `Iterator` here
15 impl<'a> StatementIterator<'a> {
16     #[allow(clippy::new_ret_no_self)]
new(stmt: &'a mut Statement, types: Vec<(MysqlType, IsSigned)>) -> QueryResult<Self>17     pub fn new(stmt: &'a mut Statement, types: Vec<(MysqlType, IsSigned)>) -> QueryResult<Self> {
18         let mut output_binds = Binds::from_output_types(types);
19 
20         execute_statement(stmt, &mut output_binds)?;
21 
22         Ok(StatementIterator {
23             stmt: stmt,
24             output_binds: output_binds,
25         })
26     }
27 
map<F, T>(mut self, mut f: F) -> QueryResult<Vec<T>> where F: FnMut(MysqlRow) -> QueryResult<T>,28     pub fn map<F, T>(mut self, mut f: F) -> QueryResult<Vec<T>>
29     where
30         F: FnMut(MysqlRow) -> QueryResult<T>,
31     {
32         let mut results = Vec::new();
33         while let Some(row) = self.next() {
34             results.push(f(row?)?);
35         }
36         Ok(results)
37     }
38 
next(&mut self) -> Option<QueryResult<MysqlRow>>39     fn next(&mut self) -> Option<QueryResult<MysqlRow>> {
40         match populate_row_buffers(self.stmt, &mut self.output_binds) {
41             Ok(Some(())) => Some(Ok(MysqlRow {
42                 col_idx: 0,
43                 binds: &mut self.output_binds,
44             })),
45             Ok(None) => None,
46             Err(e) => Some(Err(e)),
47         }
48     }
49 }
50 
51 pub struct MysqlRow<'a> {
52     col_idx: usize,
53     binds: &'a Binds,
54 }
55 
56 impl<'a> Row<Mysql> for MysqlRow<'a> {
take(&mut self) -> Option<&[u8]>57     fn take(&mut self) -> Option<&[u8]> {
58         let current_idx = self.col_idx;
59         self.col_idx += 1;
60         self.binds.field_data(current_idx)
61     }
62 
next_is_null(&self, count: usize) -> bool63     fn next_is_null(&self, count: usize) -> bool {
64         (0..count).all(|i| self.binds.field_data(self.col_idx + i).is_none())
65     }
66 }
67 
68 pub struct NamedStatementIterator<'a> {
69     stmt: &'a mut Statement,
70     output_binds: Binds,
71     metadata: StatementMetadata,
72 }
73 
74 #[allow(clippy::should_implement_trait)] // don't need `Iterator` here
75 impl<'a> NamedStatementIterator<'a> {
76     #[allow(clippy::new_ret_no_self)]
new(stmt: &'a mut Statement) -> QueryResult<Self>77     pub fn new(stmt: &'a mut Statement) -> QueryResult<Self> {
78         let metadata = stmt.metadata()?;
79         let mut output_binds = Binds::from_result_metadata(metadata.fields());
80 
81         execute_statement(stmt, &mut output_binds)?;
82 
83         Ok(NamedStatementIterator {
84             stmt,
85             output_binds,
86             metadata,
87         })
88     }
89 
map<F, T>(mut self, mut f: F) -> QueryResult<Vec<T>> where F: FnMut(NamedMysqlRow) -> QueryResult<T>,90     pub fn map<F, T>(mut self, mut f: F) -> QueryResult<Vec<T>>
91     where
92         F: FnMut(NamedMysqlRow) -> QueryResult<T>,
93     {
94         let mut results = Vec::new();
95         while let Some(row) = self.next() {
96             results.push(f(row?)?);
97         }
98         Ok(results)
99     }
100 
next(&mut self) -> Option<QueryResult<NamedMysqlRow>>101     fn next(&mut self) -> Option<QueryResult<NamedMysqlRow>> {
102         match populate_row_buffers(self.stmt, &mut self.output_binds) {
103             Ok(Some(())) => Some(Ok(NamedMysqlRow {
104                 binds: &self.output_binds,
105                 column_indices: self.metadata.column_indices(),
106             })),
107             Ok(None) => None,
108             Err(e) => Some(Err(e)),
109         }
110     }
111 }
112 
113 pub struct NamedMysqlRow<'a> {
114     binds: &'a Binds,
115     column_indices: &'a HashMap<&'a str, usize>,
116 }
117 
118 impl<'a> NamedRow<Mysql> for NamedMysqlRow<'a> {
index_of(&self, column_name: &str) -> Option<usize>119     fn index_of(&self, column_name: &str) -> Option<usize> {
120         self.column_indices.get(column_name).cloned()
121     }
122 
get_raw_value(&self, idx: usize) -> Option<&[u8]>123     fn get_raw_value(&self, idx: usize) -> Option<&[u8]> {
124         self.binds.field_data(idx)
125     }
126 }
127 
execute_statement(stmt: &mut Statement, binds: &mut Binds) -> QueryResult<()>128 fn execute_statement(stmt: &mut Statement, binds: &mut Binds) -> QueryResult<()> {
129     unsafe {
130         binds.with_mysql_binds(|bind_ptr| stmt.bind_result(bind_ptr))?;
131         stmt.execute()?;
132     }
133     Ok(())
134 }
135 
populate_row_buffers(stmt: &Statement, binds: &mut Binds) -> QueryResult<Option<()>>136 fn populate_row_buffers(stmt: &Statement, binds: &mut Binds) -> QueryResult<Option<()>> {
137     let next_row_result = unsafe { ffi::mysql_stmt_fetch(stmt.stmt.as_ptr()) };
138     match next_row_result as libc::c_uint {
139         ffi::MYSQL_NO_DATA => Ok(None),
140         ffi::MYSQL_DATA_TRUNCATED => binds.populate_dynamic_buffers(stmt).map(Some),
141         0 => {
142             binds.update_buffer_lengths();
143             Ok(Some(()))
144         }
145         _error => stmt.did_an_error_occur().map(Some),
146     }
147 }
148