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