Skip to content

Commit 7eec54b

Browse files
author
Julius de Bruijn
authored
Late night float fun (prisma#249)
Fixes all kinds of small conversion errors on PostgreSQL numeric, real and float types.
1 parent 72f660d commit 7eec54b

File tree

8 files changed

+118
-71
lines changed

8 files changed

+118
-71
lines changed

src/connector/postgres/conversion.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,12 +499,29 @@ impl<'a> ToSql for Value<'a> {
499499
let res = match (self, ty) {
500500
(Value::Integer(integer), &PostgresType::INT2) => integer.map(|integer| (integer as i16).to_sql(ty, out)),
501501
(Value::Integer(integer), &PostgresType::INT4) => integer.map(|integer| (integer as i32).to_sql(ty, out)),
502+
#[cfg(feature = "bigdecimal")]
503+
(Value::Integer(integer), &PostgresType::NUMERIC) => integer
504+
.map(|integer| BigDecimal::from_i64(integer).unwrap())
505+
.map(|bd| DecimalWrapper(bd))
506+
.map(|dw| dw.to_sql(ty, out)),
502507
(Value::Integer(integer), &PostgresType::TEXT) => {
503508
integer.map(|integer| format!("{}", integer).to_sql(ty, out))
504509
}
505510
(Value::Integer(integer), &PostgresType::OID) => integer.map(|integer| (integer as u32).to_sql(ty, out)),
506511
(Value::Integer(integer), _) => integer.map(|integer| (integer as i64).to_sql(ty, out)),
512+
(Value::Float(float), &PostgresType::FLOAT8) => float.map(|float| (float as f64).to_sql(ty, out)),
513+
#[cfg(feature = "bigdecimal")]
514+
(Value::Float(float), &PostgresType::NUMERIC) => float
515+
.map(|float| BigDecimal::from_f32(float).unwrap())
516+
.map(DecimalWrapper)
517+
.map(|dw| dw.to_sql(ty, out)),
507518
(Value::Float(float), _) => float.map(|float| float.to_sql(ty, out)),
519+
(Value::Double(double), &PostgresType::FLOAT4) => double.map(|double| (double as f32).to_sql(ty, out)),
520+
#[cfg(feature = "bigdecimal")]
521+
(Value::Double(double), &PostgresType::NUMERIC) => double
522+
.map(|double| BigDecimal::from_f64(double).unwrap())
523+
.map(DecimalWrapper)
524+
.map(|dw| dw.to_sql(ty, out)),
508525
(Value::Double(double), _) => double.map(|double| double.to_sql(ty, out)),
509526
#[cfg(feature = "bigdecimal")]
510527
(Value::Numeric(decimal), &PostgresType::FLOAT4) => decimal.as_ref().map(|decimal| {

src/tests/query.rs

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -502,7 +502,7 @@ async fn single_default_value_insert(api: &mut dyn TestApi) -> crate::Result<()>
502502
}
503503

504504
#[cfg(any(feature = "mssql", feature = "postgresql"))]
505-
#[test_each_connector(tags("mssql", "postgres"))]
505+
#[test_each_connector(tags("mssql", "postgresql"))]
506506
async fn returning_insert(api: &mut dyn TestApi) -> crate::Result<()> {
507507
let table = api.create_table("id int, name varchar(255)").await?;
508508
let insert = Insert::single_into(&table).value("id", 2).value("name", "Naukio");
@@ -647,7 +647,7 @@ async fn single_insert_conflict_do_nothing_single_unique_with_autogen_default(
647647
}
648648

649649
#[cfg(any(feature = "mssql", feature = "postgresql"))]
650-
#[test_each_connector(tags("postgres", "mssql"))]
650+
#[test_each_connector(tags("postgresql", "mssql"))]
651651
async fn single_insert_conflict_do_nothing_with_returning(api: &mut dyn TestApi) -> crate::Result<()> {
652652
let constraint = api.unique_constraint("id");
653653

@@ -1073,7 +1073,7 @@ async fn unsigned_integers_are_handled(api: &mut dyn TestApi) -> crate::Result<(
10731073
}
10741074

10751075
#[cfg(feature = "json")]
1076-
#[test_each_connector(tags("mysql", "postgres"))]
1076+
#[test_each_connector(tags("mysql", "postgresql"))]
10771077
async fn json_filtering_works(api: &mut dyn TestApi) -> crate::Result<()> {
10781078
let json_type = match api.system() {
10791079
"postgres" => "jsonb",
@@ -1117,7 +1117,7 @@ async fn json_filtering_works(api: &mut dyn TestApi) -> crate::Result<()> {
11171117
Ok(())
11181118
}
11191119

1120-
#[test_each_connector(tags("mssql", "postgres"))]
1120+
#[test_each_connector(tags("mssql", "postgresql"))]
11211121
async fn xml_filtering_works(api: &mut dyn TestApi) -> crate::Result<()> {
11221122
let table = api
11231123
.create_table(&format!("{}, xmlfield {}", api.autogen_id("id"), "xml"))
@@ -1306,14 +1306,14 @@ async fn op_test_div_one_level(api: &mut dyn TestApi) -> crate::Result<()> {
13061306
let row = api.conn().select(q).await?.into_single()?;
13071307

13081308
match api.system() {
1309-
"mssql" => assert_eq!(Some(2.0), row[0].as_f32()),
1309+
"mssql" | "postgres" => assert_eq!(Some(2.0), row[0].as_f32()),
13101310
_ => assert_eq!(Some(2.0), row[0].as_f64()),
13111311
}
13121312

13131313
Ok(())
13141314
}
13151315

1316-
#[test_each_connector(tags("postgres"))]
1316+
#[test_each_connector(tags("postgresql"))]
13171317
async fn enum_values(api: &mut dyn TestApi) -> crate::Result<()> {
13181318
let type_name = api.get_name();
13191319
let create_type = format!("CREATE TYPE {} AS ENUM ('A', 'B')", &type_name);
@@ -1615,3 +1615,32 @@ async fn insert_default_keyword(api: &mut dyn TestApi) -> crate::Result<()> {
16151615

16161616
Ok(())
16171617
}
1618+
1619+
#[cfg(feature = "bigdecimal")]
1620+
#[test_each_connector(tags("postgresql"))]
1621+
async fn ints_read_write_to_numeric(api: &mut dyn TestApi) -> crate::Result<()> {
1622+
use bigdecimal::BigDecimal;
1623+
use std::str::FromStr;
1624+
1625+
let table = api.create_table("id int, value numeric(12,2)").await?;
1626+
1627+
let insert = Insert::multi_into(&table, &["id", "value"])
1628+
.values(vec![Value::integer(1), Value::double(1234.5)])
1629+
.values(vec![Value::integer(2), Value::integer(1234)])
1630+
.values(vec![Value::integer(3), Value::integer(12345)]);
1631+
1632+
api.conn().execute(insert.into()).await?;
1633+
1634+
let select = Select::from_table(&table);
1635+
let rows = api.conn().select(select).await?;
1636+
1637+
for (i, row) in rows.into_iter().enumerate() {
1638+
match i {
1639+
0 => assert_eq!(Value::numeric(BigDecimal::from_str("1234.5").unwrap()), row["value"]),
1640+
1 => assert_eq!(Value::numeric(BigDecimal::from_str("1234.0").unwrap()), row["value"]),
1641+
_ => assert_eq!(Value::numeric(BigDecimal::from_str("12345.0").unwrap()), row["value"]),
1642+
}
1643+
}
1644+
1645+
Ok(())
1646+
}

src/tests/query/error.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ async fn bigint_unsigned_positive_value_out_of_range(api: &mut dyn TestApi) -> c
173173
Ok(())
174174
}
175175

176-
#[test_each_connector(tags("mysql", "mssql", "postgres"))]
176+
#[test_each_connector(tags("mysql", "mssql", "postgresql"))]
177177
async fn length_mismatch(api: &mut dyn TestApi) -> crate::Result<()> {
178178
let table = api.create_table("value varchar(3)").await?;
179179
let insert = Insert::single_into(&table).value("value", "fooo");
@@ -187,7 +187,7 @@ async fn length_mismatch(api: &mut dyn TestApi) -> crate::Result<()> {
187187
Ok(())
188188
}
189189

190-
#[test_each_connector(tags("postgres", "sqlite"))]
190+
#[test_each_connector(tags("postgresql", "sqlite"))]
191191
async fn foreign_key_constraint_violation(api: &mut dyn TestApi) -> crate::Result<()> {
192192
let parent = api.create_table("id smallint not null primary key").await?;
193193
let foreign_key = api.foreign_key(&parent, "id", "parent_id");

src/tests/test_api/postgres.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::env;
66

77
pub static CONN_STR: Lazy<String> = Lazy::new(|| env::var("TEST_PSQL").expect("TEST_PSQL env var"));
88

9-
pub(crate) async fn postgres_test_api<'a>() -> crate::Result<PostgreSql<'a>> {
9+
pub(crate) async fn postgresql_test_api<'a>() -> crate::Result<PostgreSql<'a>> {
1010
PostgreSql::new().await
1111
}
1212

0 commit comments

Comments
 (0)