Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
We now also generate the necessary `FromSql`/`ToSql` impls for
`chrono`/`time` types for `Time`/`Date`/`Timestamp`. This follows the
same reasoning as #diesel-rs#3747.
  • Loading branch information
weiznich committed Aug 16, 2023
1 parent 02a37df commit 77c906a
Show file tree
Hide file tree
Showing 5 changed files with 272 additions and 32 deletions.
2 changes: 2 additions & 0 deletions diesel/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ returning_clauses_for_sqlite_3_35 = []
i-implement-a-third-party-backend-and-opt-into-breaking-changes = []
nightly-error-messages = []
r2d2 = ["diesel_derives/r2d2", "dep:r2d2"]
chrono = ["diesel_derives/chrono", "dep:chrono"]
time = ["diesel_derives/time", "dep:time"]

[package.metadata.docs.rs]
features = ["postgres", "mysql", "sqlite", "extras"]
Expand Down
8 changes: 8 additions & 0 deletions diesel/src/internal/derives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,12 @@ pub mod multiconnection {

#[doc(hidden)]
pub use crate::query_builder::select_statement::SelectStatementAccessor;

#[doc(hidden)]
#[cfg(feature = "chrono")]
pub use chrono;

#[doc(hidden)]
#[cfg(feature = "time")]
pub use time;
}
8 changes: 5 additions & 3 deletions diesel_derives/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,14 @@ name = "tests"
[features]
default = []
nightly = ["proc-macro2/nightly"]
postgres = []
sqlite = []
mysql = []
postgres = ["diesel/postgres"]
sqlite = ["diesel/sqlite"]
mysql = ["diesel/mysql"]
32-column-tables = []
64-column-tables = ["32-column-tables"]
128-column-tables = ["64-column-tables"]
without-deprecated = []
with-deprecated = []
r2d2 = []
chrono = ["diesel/chrono"]
time = ["diesel/time"]
72 changes: 64 additions & 8 deletions diesel_derives/src/multiconnection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -565,7 +565,7 @@ fn generate_row(connection_types: &[ConnectionVariant]) -> TokenStream {
}

fn generate_bind_collector(connection_types: &[ConnectionVariant]) -> TokenStream {
let to_sql_impls = vec![
let mut to_sql_impls = vec![
(
quote::quote!(diesel::sql_types::SmallInt),
quote::quote!(i16),
Expand All @@ -583,11 +583,40 @@ fn generate_bind_collector(connection_types: &[ConnectionVariant]) -> TokenStrea
quote::quote!([u8]),
),
(quote::quote!(diesel::sql_types::Bool), quote::quote!(bool)),
]
.into_iter()
.map(|t| generate_to_sql_impls(t, connection_types));
];
if cfg!(feature = "chrono") {
to_sql_impls.push((
quote::quote!(diesel::sql_types::Timestamp),
quote::quote!(diesel::internal::derives::multiconnection::chrono::NaiveDateTime),
));
to_sql_impls.push((
quote::quote!(diesel::sql_types::Date),
quote::quote!(diesel::internal::derives::multiconnection::chrono::NaiveDate),
));
to_sql_impls.push((
quote::quote!(diesel::sql_types::Time),
quote::quote!(diesel::internal::derives::multiconnection::chrono::NaiveTime),
));
}
if cfg!(feature = "time") {
to_sql_impls.push((
quote::quote!(diesel::sql_types::Timestamp),
quote::quote!(diesel::internal::derives::multiconnection::time::PrimitiveDateTime),
));
to_sql_impls.push((
quote::quote!(diesel::sql_types::Time),
quote::quote!(diesel::internal::derives::multiconnection::time::Time),
));
to_sql_impls.push((
quote::quote!(diesel::sql_types::Date),
quote::quote!(diesel::internal::derives::multiconnection::time::Date),
));
}
let to_sql_impls = to_sql_impls
.into_iter()
.map(|t| generate_to_sql_impls(t, connection_types));

let from_sql_impls = vec![
let mut from_sql_impls = vec![
(
quote::quote!(diesel::sql_types::SmallInt),
quote::quote!(i16),
Expand All @@ -608,9 +637,36 @@ fn generate_bind_collector(connection_types: &[ConnectionVariant]) -> TokenStrea
quote::quote!(Vec<u8>),
),
(quote::quote!(diesel::sql_types::Bool), quote::quote!(bool)),
]
.into_iter()
.map(generate_from_sql_impls);
];
if cfg!(feature = "chrono") {
from_sql_impls.push((
quote::quote!(diesel::sql_types::Timestamp),
quote::quote!(diesel::internal::derives::multiconnection::chrono::NaiveDateTime),
));
from_sql_impls.push((
quote::quote!(diesel::sql_types::Date),
quote::quote!(diesel::internal::derives::multiconnection::chrono::NaiveDate),
));
from_sql_impls.push((
quote::quote!(diesel::sql_types::Time),
quote::quote!(diesel::internal::derives::multiconnection::chrono::NaiveTime),
));
}
if cfg!(feature = "time") {
from_sql_impls.push((
quote::quote!(diesel::sql_types::Timestamp),
quote::quote!(diesel::internal::derives::multiconnection::time::PrimitiveDateTime),
));
from_sql_impls.push((
quote::quote!(diesel::sql_types::Time),
quote::quote!(diesel::internal::derives::multiconnection::time::Time),
));
from_sql_impls.push((
quote::quote!(diesel::sql_types::Date),
quote::quote!(diesel::internal::derives::multiconnection::time::Date),
));
}
let from_sql_impls = from_sql_impls.into_iter().map(generate_from_sql_impls);

let into_bind_value_bounds = connection_types.iter().map(|c| {
let ty = c.ty;
Expand Down
214 changes: 193 additions & 21 deletions diesel_derives/tests/multiconnection.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use crate::schema::users;
use diesel::prelude::*;
use diesel::sql_types::{Binary, Float, Integer, Nullable, Text};

#[derive(diesel::MultiConnection)]
pub enum InferConnection {
Expand All @@ -20,16 +19,7 @@ pub struct User {

#[test]
fn check_queries_work() {
let database_url = if cfg!(feature = "mysql") {
dotenvy::var("MYSQL_UNIT_TEST_DATABASE_URL").or_else(|_| dotenvy::var("DATABASE_URL"))
} else if cfg!(feature = "postgres") {
dotenvy::var("PG_DATABASE_URL").or_else(|_| dotenvy::var("DATABASE_URL"))
} else {
Ok(dotenvy::var("DATABASE_URL").unwrap_or_else(|_| ":memory:".to_owned()))
};
let database_url = database_url.expect("DATABASE_URL must be set in order to run tests");

let mut conn = InferConnection::establish(&database_url).unwrap();
let mut conn = establish_connection();

diesel::sql_query(
"CREATE TEMPORARY TABLE users(\
Expand Down Expand Up @@ -106,19 +96,201 @@ fn check_queries_work() {

// delete
diesel::delete(users::table).execute(&mut conn).unwrap();
}

// more binds
// mostly nullable types
let (string, int, blob, float) = diesel::select((
None::<String>.into_sql::<Nullable<Text>>(),
fn establish_connection() -> InferConnection {
let database_url = if cfg!(feature = "mysql") {
dotenvy::var("MYSQL_UNIT_TEST_DATABASE_URL").or_else(|_| dotenvy::var("DATABASE_URL"))
} else if cfg!(feature = "postgres") {
dotenvy::var("PG_DATABASE_URL").or_else(|_| dotenvy::var("DATABASE_URL"))
} else {
Ok(dotenvy::var("DATABASE_URL").unwrap_or_else(|_| ":memory:".to_owned()))
};
let database_url = database_url.expect("DATABASE_URL must be set in order to run tests");

let mut conn = InferConnection::establish(&database_url).unwrap();
conn
}

#[cfg(all(feature = "chrono", feature = "time"))]
#[test]
fn type_checks() {
use diesel::internal::derives::multiconnection::{chrono, time};
use diesel::sql_types::*;

let mut conn = establish_connection();
conn.begin_test_transaction().unwrap();
let small_int = 1_i16;
let integer = 2_i32;
let big_int = 3_i64;
let float = 4.0_f32;
let double = 5.0_f64;
let string = String::from("bar");
let blob = vec![1_u8, 2, 3, 4];
let timestamp1 = chrono::Utc::now().naive_utc();
let time1 = chrono::Utc::now().time();
let date1 = chrono::Utc::now().date_naive();
let time2 = time::Time::from_hms(12, 22, 23).unwrap();
let date2 = time::Date::from_calendar_date(2023, time::Month::August, 26).unwrap();
let timestamp2 = time::PrimitiveDateTime::new(date2, time2);

let result = diesel::select((
small_int.into_sql::<SmallInt>(),
integer.into_sql::<Integer>(),
big_int.into_sql::<BigInt>(),
float.into_sql::<Float>(),
double.into_sql::<Double>(),
string.as_sql::<Text>(),
blob.as_sql::<Blob>(),
timestamp1.into_sql::<Timestamp>(),
time1.into_sql::<Time>(),
date1.into_sql::<Date>(),
timestamp2.into_sql::<Timestamp>(),
time2.into_sql::<Time>(),
date2.into_sql::<Date>(),
))
.get_result::<(
i16,//0
i32,//1
i64,//2
f32,//3
f64,//4
String,//5
Vec<u8>,//6
chrono::NaiveDateTime,//7
chrono::NaiveTime,//8
chrono::NaiveDate,//9
time::PrimitiveDateTime,//10
time::Time,//11
time::Date,//12
)>(&mut conn)
.unwrap();

assert_eq!(small_int, result.0);
assert_eq!(integer, result.1);
assert_eq!(big_int, result.2);
assert_eq!(float, result.3);
assert_eq!(double, result.4);
assert_eq!(string, result.5);
assert_eq!(blob, result.6);
assert_eq!(timestamp1, result.7);
assert_eq!(time1, result.8);
assert_eq!(date1, result.9);
assert_eq!(timestamp2, result.10);
assert_eq!(time2, result.11);
assert_eq!(date2, result.12);
}

#[cfg(all(feature = "chrono", feature = "time"))]
#[test]
fn nullable_type_checks() {
use diesel::internal::derives::multiconnection::{chrono, time};
use diesel::sql_types::*;
let mut conn = establish_connection();
conn.begin_test_transaction().unwrap();

let small_int = Some(1_i16);
let integer = Some(2_i32);
let big_int = Some(3_i64);
let float = Some(4.0_f32);
let double = Some(5.0_f64);
let string = Some(String::from("bar"));
let blob = Some(vec![1_u8, 2, 3, 4]);
let timestamp1 = Some(chrono::Utc::now().naive_utc());
let time1 = Some(chrono::Utc::now().time());
let date1 = Some(chrono::Utc::now().date_naive());
let time2 = Some(time::Time::from_hms(12, 22, 23).unwrap());
let date2 = Some(time::Date::from_calendar_date(2023, time::Month::August, 26).unwrap());
let timestamp2 = Some(time::PrimitiveDateTime::new(date2.unwrap(), time2.unwrap()));

let result = diesel::select((
small_int.into_sql::<Nullable<SmallInt>>(),
integer.into_sql::<Nullable<Integer>>(),
big_int.into_sql::<Nullable<BigInt>>(),
float.into_sql::<Nullable<Float>>(),
double.into_sql::<Nullable<Double>>(),
string.as_sql::<Nullable<Text>>(),
blob.as_sql::<Nullable<Blob>>(),
timestamp1.into_sql::<Nullable<Timestamp>>(),
time1.into_sql::<Nullable<Time>>(),
date1.into_sql::<Nullable<Date>>(),
timestamp2.into_sql::<Nullable<Timestamp>>(),
time2.into_sql::<Nullable<Time>>(),
date2.into_sql::<Nullable<Date>>(),
))
.get_result::<(
Option<i16>,
Option<i32>,
Option<i64>,
Option<f32>,
Option<f64>,
Option<String>,
Option<Vec<u8>>,
Option<chrono::NaiveDateTime>,
Option<chrono::NaiveTime>,
Option<chrono::NaiveDate>,
Option<time::PrimitiveDateTime>,
Option<time::Time>,
Option<time::Date>,
)>(&mut conn)
.unwrap();

assert_eq!(small_int, result.0);
assert_eq!(integer, result.1);
assert_eq!(big_int, result.2);
assert_eq!(float, result.3);
assert_eq!(double, result.4);
assert_eq!(string, result.5);
assert_eq!(blob, result.6);
assert_eq!(timestamp1, result.7);
assert_eq!(time1, result.8);
assert_eq!(date1, result.9);
assert_eq!(timestamp2, result.10);
assert_eq!(time2, result.11);
assert_eq!(date2, result.12);

let result = diesel::select((
None::<i16>.into_sql::<Nullable<SmallInt>>(),
None::<i32>.into_sql::<Nullable<Integer>>(),
None::<i64>.into_sql::<Nullable<BigInt>>(),
None::<f32>.into_sql::<Nullable<Float>>(),
None::<Vec<u8>>.into_sql::<Nullable<Binary>>(),
None::<f64>.into_sql::<Nullable<Double>>(),
None::<String>.into_sql::<Nullable<Text>>(),
None::<Vec<_>>.into_sql::<Nullable<Blob>>(),
None::<chrono::NaiveDateTime>.into_sql::<Nullable<Timestamp>>(),
None::<chrono::NaiveTime>.into_sql::<Nullable<Time>>(),
None::<chrono::NaiveDate>.into_sql::<Nullable<Date>>(),
None::<time::PrimitiveDateTime>.into_sql::<Nullable<Timestamp>>(),
None::<time::Time>.into_sql::<Nullable<Time>>(),
None::<time::Date>.into_sql::<Nullable<Date>>(),
))
.get_result::<(Option<String>, Option<i32>, Option<f32>, Option<Vec<u8>>)>(&mut conn)
.get_result::<(
Option<i16>,
Option<i32>,
Option<i64>,
Option<f32>,
Option<f64>,
Option<String>,
Option<Vec<u8>>,
Option<chrono::NaiveDateTime>,
Option<chrono::NaiveTime>,
Option<chrono::NaiveDate>,
Option<time::PrimitiveDateTime>,
Option<time::Time>,
Option<time::Date>,
)>(&mut conn)
.unwrap();
assert!(string.is_none());
assert!(int.is_none());
assert!(float.is_none());
assert!(blob.is_none());
assert!(result.0.is_none());
assert!(result.1.is_none());
assert!(result.2.is_none());
assert!(result.3.is_none());
assert!(result.4.is_none());
assert!(result.5.is_none());
assert!(result.6.is_none());
assert!(result.7.is_none());
assert!(result.8.is_none());
assert!(result.9.is_none());
assert!(result.10.is_none());
assert!(result.11.is_none());
assert!(result.12.is_none());
}

0 comments on commit 77c906a

Please sign in to comment.