Skip to content

Commit f714fdc

Browse files
committed
Enable a few more lints regarding truncations in numerical casts
This is similar to diesel-rs/diesel#4170, it's just not a serve as the diesel change as we do not found any critical cast here. I also investigated the implementation in the postgres crate and it seems to be fine as well (i.e error on too large buffer sizes instead silently truncating)
1 parent dbdcbc2 commit f714fdc

File tree

6 files changed

+53
-28
lines changed

6 files changed

+53
-28
lines changed

src/lib.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,12 @@
6666
//! # }
6767
//! ```
6868
69-
#![warn(missing_docs)]
69+
#![warn(
70+
missing_docs,
71+
clippy::cast_possible_wrap,
72+
clippy::cast_possible_truncation,
73+
clippy::cast_sign_loss
74+
)]
7075

7176
use diesel::backend::Backend;
7277
use diesel::connection::Instrumentation;

src/mysql/mod.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,8 @@ impl AsyncConnection for AsyncMysqlConnection {
150150
+ 'query,
151151
{
152152
self.with_prepared_statement(source, |conn, stmt, binds| async move {
153-
conn.exec_drop(&*stmt, binds).await.map_err(ErrorHelper)?;
153+
let params = mysql_async::Params::try_from(binds)?;
154+
conn.exec_drop(&*stmt, params).await.map_err(ErrorHelper)?;
154155
// We need to close any non-cached statement explicitly here as otherwise
155156
// we might error out on too many open statements. See https://github.com/weiznich/diesel_async/issues/26
156157
// for details
@@ -165,7 +166,9 @@ impl AsyncConnection for AsyncMysqlConnection {
165166
if let MaybeCached::CannotCache(stmt) = stmt {
166167
conn.close(stmt).await.map_err(ErrorHelper)?;
167168
}
168-
Ok(conn.affected_rows() as usize)
169+
conn.affected_rows()
170+
.try_into()
171+
.map_err(|e| diesel::result::Error::DeserializationError(Box::new(e)))
169172
})
170173
}
171174

@@ -325,8 +328,10 @@ impl AsyncMysqlConnection {
325328
mut tx: futures_channel::mpsc::Sender<QueryResult<MysqlRow>>,
326329
) -> QueryResult<()> {
327330
use futures_util::sink::SinkExt;
331+
let params = mysql_async::Params::try_from(binds)?;
332+
328333
let res = conn
329-
.exec_iter(stmt_for_exec, binds)
334+
.exec_iter(stmt_for_exec, params)
330335
.await
331336
.map_err(ErrorHelper)?;
332337

src/mysql/row.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,12 @@ impl<'a> diesel::row::Row<'a, Mysql> for MysqlRow {
9999
Some(Cow::Owned(buffer))
100100
}
101101
_t => {
102-
let mut buffer = Vec::with_capacity(value.bin_len() as usize);
102+
let mut buffer = Vec::with_capacity(
103+
value
104+
.bin_len()
105+
.try_into()
106+
.expect("Failed to cast byte size to usize"),
107+
);
103108
mysql_common::proto::MySerialize::serialize(value, &mut buffer);
104109
Some(Cow::Owned(buffer))
105110
}

src/mysql/serialize.rs

+29-20
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use diesel::mysql::data_types::MysqlTime;
22
use diesel::mysql::MysqlType;
33
use diesel::mysql::MysqlValue;
4+
use diesel::QueryResult;
45
use mysql_async::{Params, Value};
56
use std::convert::TryInto;
67

@@ -9,10 +10,11 @@ pub(super) struct ToSqlHelper {
910
pub(super) binds: Vec<Option<Vec<u8>>>,
1011
}
1112

12-
fn to_value((metadata, bind): (MysqlType, Option<Vec<u8>>)) -> Value {
13-
match bind {
13+
fn to_value((metadata, bind): (MysqlType, Option<Vec<u8>>)) -> QueryResult<Value> {
14+
let cast_helper = |e| diesel::result::Error::SerializationError(Box::new(e));
15+
let v = match bind {
1416
Some(bind) => match metadata {
15-
MysqlType::Tiny => Value::Int((bind[0] as i8) as i64),
17+
MysqlType::Tiny => Value::Int(i8::from_be_bytes([bind[0]]) as i64),
1618
MysqlType::Short => Value::Int(i16::from_ne_bytes(bind.try_into().unwrap()) as _),
1719
MysqlType::Long => Value::Int(i32::from_ne_bytes(bind.try_into().unwrap()) as _),
1820
MysqlType::LongLong => Value::Int(i64::from_ne_bytes(bind.try_into().unwrap())),
@@ -38,11 +40,11 @@ fn to_value((metadata, bind): (MysqlType, Option<Vec<u8>>)) -> Value {
3840
.expect("This does not fail");
3941
Value::Time(
4042
time.neg,
41-
time.day as _,
42-
time.hour as _,
43-
time.minute as _,
44-
time.second as _,
45-
time.second_part as _,
43+
time.day,
44+
time.hour.try_into().map_err(cast_helper)?,
45+
time.minute.try_into().map_err(cast_helper)?,
46+
time.second.try_into().map_err(cast_helper)?,
47+
time.second_part.try_into().map_err(cast_helper)?,
4648
)
4749
}
4850
MysqlType::Date | MysqlType::DateTime | MysqlType::Timestamp => {
@@ -52,13 +54,13 @@ fn to_value((metadata, bind): (MysqlType, Option<Vec<u8>>)) -> Value {
5254
>::from_sql(MysqlValue::new(&bind, metadata))
5355
.expect("This does not fail");
5456
Value::Date(
55-
time.year as _,
56-
time.month as _,
57-
time.day as _,
58-
time.hour as _,
59-
time.minute as _,
60-
time.second as _,
61-
time.second_part as _,
57+
time.year.try_into().map_err(cast_helper)?,
58+
time.month.try_into().map_err(cast_helper)?,
59+
time.day.try_into().map_err(cast_helper)?,
60+
time.hour.try_into().map_err(cast_helper)?,
61+
time.minute.try_into().map_err(cast_helper)?,
62+
time.second.try_into().map_err(cast_helper)?,
63+
time.second_part.try_into().map_err(cast_helper)?,
6264
)
6365
}
6466
MysqlType::Numeric
@@ -70,12 +72,19 @@ fn to_value((metadata, bind): (MysqlType, Option<Vec<u8>>)) -> Value {
7072
_ => unreachable!(),
7173
},
7274
None => Value::NULL,
73-
}
75+
};
76+
Ok(v)
7477
}
7578

76-
impl From<ToSqlHelper> for Params {
77-
fn from(ToSqlHelper { metadata, binds }: ToSqlHelper) -> Self {
78-
let values = metadata.into_iter().zip(binds).map(to_value).collect();
79-
Params::Positional(values)
79+
impl TryFrom<ToSqlHelper> for Params {
80+
type Error = diesel::result::Error;
81+
82+
fn try_from(ToSqlHelper { metadata, binds }: ToSqlHelper) -> Result<Self, Self::Error> {
83+
let values = metadata
84+
.into_iter()
85+
.zip(binds)
86+
.map(to_value)
87+
.collect::<Result<Vec<_>, Self::Error>>()?;
88+
Ok(Params::Positional(values))
8089
}
8190
}

src/pg/error_helper.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,9 @@ impl diesel::result::DatabaseErrorInformation for PostgresDbErrorWrapper {
8181

8282
fn statement_position(&self) -> Option<i32> {
8383
use tokio_postgres::error::ErrorPosition;
84-
self.0.position().map(|e| match e {
84+
self.0.position().and_then(|e| match *e {
8585
ErrorPosition::Original(position) | ErrorPosition::Internal { position, .. } => {
86-
*position as i32
86+
position.try_into().ok()
8787
}
8888
})
8989
}

src/pg/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,8 @@ async fn execute_prepared(
267267
let res = tokio_postgres::Client::execute(&conn, &stmt, &binds as &[_])
268268
.await
269269
.map_err(ErrorHelper)?;
270-
Ok(res as usize)
270+
res.try_into()
271+
.map_err(|e| diesel::result::Error::DeserializationError(Box::new(e)))
271272
}
272273

273274
#[inline(always)]

0 commit comments

Comments
 (0)