Skip to content

Commit a4a70d9

Browse files
authored
Merge pull request diesel-rs#2799 from weiznich/feature/load_returns_a_iterator
Return a iterator from `Connection::load`
2 parents e24b13d + 69876a1 commit a4a70d9

54 files changed

Lines changed: 1971 additions & 676 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

diesel/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ edition = "2018"
1616
byteorder = "1.0"
1717
chrono = { version = "0.4.19", optional = true, default-features = false, features = ["clock", "std"] }
1818
libc = { version = "0.2.0", optional = true }
19-
libsqlite3-sys = { version = ">=0.8.0, <0.23.0", optional = true, features = ["min_sqlite_version_3_7_16"] }
19+
libsqlite3-sys = { version = ">=0.8.0, <0.23.0", optional = true, features = ["bundled_bindings"] }
2020
mysqlclient-sys = { version = "0.2.0", optional = true }
2121
pq-sys = { version = "0.4.0", optional = true }
2222
quickcheck = { version = "0.9.0", optional = true }
@@ -44,7 +44,7 @@ ipnetwork = ">=0.12.2, <0.19.0"
4444
quickcheck = "0.9"
4545

4646
[features]
47-
default = ["with-deprecated", "32-column-tables"]
47+
default = ["32-column-tables", "with-deprecated"]
4848
extras = ["chrono", "serde_json", "uuid", "network-address", "numeric", "r2d2"]
4949
unstable = ["diesel_derives/nightly"]
5050
large-tables = ["32-column-tables"]

diesel/src/connection/mod.rs

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,12 @@ mod transaction_manager;
66
use std::fmt::Debug;
77

88
use crate::backend::Backend;
9-
use crate::deserialize::FromSqlRow;
109
use crate::expression::QueryMetadata;
1110
use crate::query_builder::{AsQuery, QueryFragment, QueryId};
12-
use crate::query_dsl::load_dsl::CompatibleType;
1311
use crate::result::*;
1412

1513
#[doc(hidden)]
16-
pub use self::statement_cache::{MaybeCached, StatementCache, StatementCacheKey};
14+
pub use self::statement_cache::{MaybeCached, PrepareForCache, StatementCache, StatementCacheKey};
1715
pub use self::transaction_manager::{AnsiTransactionManager, TransactionManager};
1816

1917
/// Perform simple operations on a backend.
@@ -27,8 +25,25 @@ pub trait SimpleConnection {
2725
fn batch_execute(&mut self, query: &str) -> QueryResult<()>;
2826
}
2927

28+
/// This trait describes which cursor type is used by a given connection
29+
/// implementation. This trait is only useful in combination with [`Connection`].
30+
///
31+
/// Implementation wise this is a workaround for GAT's
32+
pub trait ConnectionGatWorkaround<'a, DB: Backend> {
33+
/// The cursor type returned by [`Connection::load`]
34+
///
35+
/// Users should handle this as opaque type that implements [`Iterator`]
36+
type Cursor: Iterator<Item = QueryResult<Self::Row>>;
37+
/// The row type used as [`Iterator::Item`] for the iterator implementation
38+
/// of [`ConnectionGatWorkaround::Cursor`]
39+
type Row: crate::row::Row<'a, DB>;
40+
}
41+
3042
/// A connection to a database
31-
pub trait Connection: SimpleConnection + Sized + Send {
43+
pub trait Connection: SimpleConnection + Sized + Send
44+
where
45+
Self: for<'a> ConnectionGatWorkaround<'a, <Self as Connection>::Backend>,
46+
{
3247
/// The backend this type connects to
3348
type Backend: Backend;
3449

@@ -177,12 +192,13 @@ pub trait Connection: SimpleConnection + Sized + Send {
177192
fn execute(&mut self, query: &str) -> QueryResult<usize>;
178193

179194
#[doc(hidden)]
180-
fn load<T, U, ST>(&mut self, source: T) -> QueryResult<Vec<U>>
195+
fn load<T>(
196+
&mut self,
197+
source: T,
198+
) -> QueryResult<<Self as ConnectionGatWorkaround<Self::Backend>>::Cursor>
181199
where
182200
T: AsQuery,
183201
T::Query: QueryFragment<Self::Backend> + QueryId,
184-
T::SqlType: CompatibleType<U, Self::Backend, SqlType = ST>,
185-
U: FromSqlRow<ST, Self::Backend>,
186202
Self::Backend: QueryMetadata<T::SqlType>;
187203

188204
#[doc(hidden)]

diesel/src/connection/statement_cache.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,12 @@ pub struct StatementCache<DB: Backend, Statement> {
107107
pub cache: HashMap<StatementCacheKey<DB>, Statement>,
108108
}
109109

110+
#[derive(Debug, Clone, Copy)]
111+
pub enum PrepareForCache {
112+
Yes,
113+
No,
114+
}
115+
110116
#[allow(clippy::len_without_is_empty, clippy::new_without_default)]
111117
impl<DB, Statement> StatementCache<DB, Statement>
112118
where
@@ -133,23 +139,23 @@ where
133139
) -> QueryResult<MaybeCached<Statement>>
134140
where
135141
T: QueryFragment<DB> + QueryId,
136-
F: FnOnce(&str) -> QueryResult<Statement>,
142+
F: FnOnce(&str, PrepareForCache) -> QueryResult<Statement>,
137143
{
138144
use std::collections::hash_map::Entry::{Occupied, Vacant};
139145

140146
let cache_key = StatementCacheKey::for_source(source, bind_types)?;
141147

142148
if !source.is_safe_to_cache_prepared()? {
143149
let sql = cache_key.sql(source)?;
144-
return prepare_fn(&sql).map(MaybeCached::CannotCache);
150+
return prepare_fn(&sql, PrepareForCache::No).map(MaybeCached::CannotCache);
145151
}
146152

147153
let cached_result = match self.cache.entry(cache_key) {
148154
Occupied(entry) => entry.into_mut(),
149155
Vacant(entry) => {
150156
let statement = {
151157
let sql = entry.key().sql(source)?;
152-
prepare_fn(&sql)
158+
prepare_fn(&sql, PrepareForCache::Yes)
153159
};
154160

155161
entry.insert(statement?)

diesel/src/mysql/connection/bind.rs

Lines changed: 63 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,23 @@ use crate::mysql::types::MYSQL_TIME;
1111
use crate::mysql::{MysqlType, MysqlValue};
1212
use crate::result::QueryResult;
1313

14-
pub struct Binds {
14+
pub struct PreparedStatementBinds(Binds);
15+
16+
pub struct OutputBinds(Binds);
17+
18+
impl Clone for OutputBinds {
19+
fn clone(&self) -> Self {
20+
Self(Binds {
21+
data: self.0.data.clone(),
22+
})
23+
}
24+
}
25+
26+
struct Binds {
1527
data: Vec<BindData>,
1628
}
1729

18-
impl Binds {
30+
impl PreparedStatementBinds {
1931
pub fn from_input_data<Iter>(input: Iter) -> QueryResult<Self>
2032
where
2133
Iter: IntoIterator<Item = (MysqlType, Option<Vec<u8>>)>,
@@ -25,34 +37,31 @@ impl Binds {
2537
.map(BindData::for_input)
2638
.collect::<Vec<_>>();
2739

28-
Ok(Binds { data })
40+
Ok(Self(Binds { data }))
2941
}
3042

31-
pub fn from_output_types(types: Vec<Option<MysqlType>>, metadata: &StatementMetadata) -> Self {
43+
pub fn with_mysql_binds<F, T>(&mut self, f: F) -> T
44+
where
45+
F: FnOnce(*mut ffi::MYSQL_BIND) -> T,
46+
{
47+
self.0.with_mysql_binds(f)
48+
}
49+
}
50+
51+
impl OutputBinds {
52+
pub fn from_output_types(types: &[Option<MysqlType>], metadata: &StatementMetadata) -> Self {
3253
let data = metadata
3354
.fields()
3455
.iter()
35-
.zip(types.into_iter().chain(std::iter::repeat(None)))
56+
.zip(types.iter().copied().chain(std::iter::repeat(None)))
3657
.map(|(field, tpe)| BindData::for_output(tpe, field))
3758
.collect();
3859

39-
Binds { data }
40-
}
41-
42-
pub fn with_mysql_binds<F, T>(&mut self, f: F) -> T
43-
where
44-
F: FnOnce(*mut ffi::MYSQL_BIND) -> T,
45-
{
46-
let mut binds = self
47-
.data
48-
.iter_mut()
49-
.map(|x| unsafe { x.mysql_bind() })
50-
.collect::<Vec<_>>();
51-
f(binds.as_mut_ptr())
60+
Self(Binds { data })
5261
}
5362

5463
pub fn populate_dynamic_buffers(&mut self, stmt: &Statement) -> QueryResult<()> {
55-
for (i, data) in self.data.iter_mut().enumerate() {
64+
for (i, data) in self.0.data.iter_mut().enumerate() {
5665
data.did_numeric_overflow_occur()?;
5766
// This is safe because we are re-binding the invalidated buffers
5867
// at the end of this function
@@ -69,20 +78,37 @@ impl Binds {
6978
}
7079

7180
pub fn update_buffer_lengths(&mut self) {
72-
for data in &mut self.data {
81+
for data in &mut self.0.data {
7382
data.update_buffer_length();
7483
}
7584
}
7685

77-
pub fn len(&self) -> usize {
78-
self.data.len()
86+
pub fn with_mysql_binds<F, T>(&mut self, f: F) -> T
87+
where
88+
F: FnOnce(*mut ffi::MYSQL_BIND) -> T,
89+
{
90+
self.0.with_mysql_binds(f)
91+
}
92+
}
93+
94+
impl Binds {
95+
fn with_mysql_binds<F, T>(&mut self, f: F) -> T
96+
where
97+
F: FnOnce(*mut ffi::MYSQL_BIND) -> T,
98+
{
99+
let mut binds = self
100+
.data
101+
.iter_mut()
102+
.map(|x| unsafe { x.mysql_bind() })
103+
.collect::<Vec<_>>();
104+
f(binds.as_mut_ptr())
79105
}
80106
}
81107

82-
impl Index<usize> for Binds {
108+
impl Index<usize> for OutputBinds {
83109
type Output = BindData;
84110
fn index(&self, index: usize) -> &Self::Output {
85-
&self.data[index]
111+
&self.0.data[index]
86112
}
87113
}
88114

@@ -122,7 +148,7 @@ impl From<u32> for Flags {
122148
}
123149
}
124150

125-
#[derive(Debug)]
151+
#[derive(Debug, Clone)]
126152
pub struct BindData {
127153
tpe: ffi::enum_field_types,
128154
bytes: Vec<u8>,
@@ -713,9 +739,8 @@ mod tests {
713739
)
714740
.unwrap();
715741

716-
let mut stmt = conn
717-
.prepare_query(&crate::sql_query(
718-
"SELECT
742+
let mut stmt = conn.prepared_query(&crate::sql_query(
743+
"SELECT
719744
tiny_int, small_int, medium_int, int_col,
720745
big_int, unsigned_int, zero_fill_int,
721746
numeric_col, decimal_col, float_col, double_col, bit_col,
@@ -725,30 +750,21 @@ mod tests {
725750
ST_AsText(polygon_col), ST_AsText(multipoint_col), ST_AsText(multilinestring_col),
726751
ST_AsText(multipolygon_col), ST_AsText(geometry_collection), json_col
727752
FROM all_mysql_types",
728-
))
729-
.unwrap();
753+
)).unwrap();
730754

731755
let metadata = stmt.metadata().unwrap();
732756
let mut output_binds =
733-
Binds::from_output_types(vec![None; metadata.fields().len()], &metadata);
757+
OutputBinds::from_output_types(&vec![None; metadata.fields().len()], &metadata);
734758
stmt.execute_statement(&mut output_binds).unwrap();
735759
stmt.populate_row_buffers(&mut output_binds).unwrap();
736760

737761
let results: Vec<(BindData, &_)> = output_binds
762+
.0
738763
.data
739764
.into_iter()
740765
.zip(metadata.fields())
741766
.collect::<Vec<_>>();
742767

743-
macro_rules! matches {
744-
($expression:expr, $( $pattern:pat )|+ $( if $guard: expr )?) => {
745-
match $expression {
746-
$( $pattern )|+ $( if $guard )? => true,
747-
_ => false
748-
}
749-
}
750-
}
751-
752768
let tiny_int_col = &results[0].0;
753769
assert_eq!(tiny_int_col.tpe, ffi::enum_field_types::MYSQL_TYPE_TINY);
754770
assert!(tiny_int_col.flags.contains(Flags::NUM_FLAG));
@@ -1057,9 +1073,9 @@ mod tests {
10571073
assert!(!polygon_col.flags.contains(Flags::ENUM_FLAG));
10581074
assert!(!polygon_col.flags.contains(Flags::BINARY_FLAG));
10591075
assert_eq!(
1060-
to_value::<Text, String>(polygon_col).unwrap(),
1061-
"MULTIPOLYGON(((28 26,28 0,84 0,84 42,28 26),(52 18,66 23,73 9,48 6,52 18)),((59 18,67 18,67 13,59 13,59 18)))"
1062-
);
1076+
to_value::<Text, String>(polygon_col).unwrap(),
1077+
"MULTIPOLYGON(((28 26,28 0,84 0,84 42,28 26),(52 18,66 23,73 9,48 6,52 18)),((59 18,67 18,67 13,59 13,59 18)))"
1078+
);
10631079

10641080
let geometry_collection = &results[32].0;
10651081
assert_eq!(
@@ -1105,12 +1121,12 @@ mod tests {
11051121

11061122
let bind = BindData::for_test_output(bind_tpe.into());
11071123

1108-
let mut binds = Binds { data: vec![bind] };
1124+
let mut binds = OutputBinds(Binds { data: vec![bind] });
11091125

11101126
stmt.execute_statement(&mut binds).unwrap();
11111127
stmt.populate_row_buffers(&mut binds).unwrap();
11121128

1113-
binds.data.remove(0)
1129+
binds.0.data.remove(0)
11141130
}
11151131

11161132
fn input_bind(
@@ -1144,9 +1160,9 @@ mod tests {
11441160
is_truncated: None,
11451161
};
11461162

1147-
let binds = Binds {
1163+
let binds = PreparedStatementBinds(Binds {
11481164
data: vec![id_bind, field_bind],
1149-
};
1165+
});
11501166
stmt.input_bind(binds).unwrap();
11511167
stmt.did_an_error_occur().unwrap();
11521168
unsafe {

0 commit comments

Comments
 (0)