Skip to content

Commit

Permalink
Merge pull request #1816 from itowlson/pg-null-was-too-typeful
Browse files Browse the repository at this point in the history
Fix Postgres NULL attempting type conversion
  • Loading branch information
itowlson authored Sep 27, 2023
2 parents 8e283e0 + 38151d8 commit 2688833
Showing 1 changed file with 43 additions and 3 deletions.
46 changes: 43 additions & 3 deletions crates/outbound-pg/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,6 @@ impl postgres::Host for OutboundPg {
}
}

const DB_NULL: Option<i32> = None;

fn to_sql_parameter(value: &ParameterValue) -> anyhow::Result<&(dyn ToSql + Sync)> {
match value {
ParameterValue::Boolean(v) => Ok(v),
Expand All @@ -120,7 +118,7 @@ fn to_sql_parameter(value: &ParameterValue) -> anyhow::Result<&(dyn ToSql + Sync
| ParameterValue::Uint64(_) => Err(anyhow!("Postgres does not support unsigned integers")),
ParameterValue::Str(v) => Ok(v),
ParameterValue::Binary(v) => Ok(v),
ParameterValue::DbNull => Ok(&DB_NULL),
ParameterValue::DbNull => Ok(&PgNull),
}
}

Expand Down Expand Up @@ -285,3 +283,45 @@ where
}
});
}

/// Although the Postgres crate converts Rust Option::None to Postgres NULL,
/// it enforces the type of the Option as it does so. (For example, trying to
/// pass an Option::<i32>::None to a VARCHAR column fails conversion.) As we
/// do not know expected column types, we instead use a "neutral" custom type
/// which allows conversion to any type but always tells the Postgres crate to
/// treat it as a SQL NULL.
struct PgNull;

impl ToSql for PgNull {
fn to_sql(
&self,
_ty: &Type,
_out: &mut tokio_postgres::types::private::BytesMut,
) -> Result<tokio_postgres::types::IsNull, Box<dyn std::error::Error + Sync + Send>>
where
Self: Sized,
{
Ok(tokio_postgres::types::IsNull::Yes)
}

fn accepts(_ty: &Type) -> bool
where
Self: Sized,
{
true
}

fn to_sql_checked(
&self,
_ty: &Type,
_out: &mut tokio_postgres::types::private::BytesMut,
) -> Result<tokio_postgres::types::IsNull, Box<dyn std::error::Error + Sync + Send>> {
Ok(tokio_postgres::types::IsNull::Yes)
}
}

impl std::fmt::Debug for PgNull {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("NULL").finish()
}
}

0 comments on commit 2688833

Please sign in to comment.