Skip to content

Commit 2688833

Browse files
authored
Merge pull request #1816 from itowlson/pg-null-was-too-typeful
Fix Postgres NULL attempting type conversion
2 parents 8e283e0 + 38151d8 commit 2688833

File tree

1 file changed

+43
-3
lines changed

1 file changed

+43
-3
lines changed

crates/outbound-pg/src/lib.rs

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,6 @@ impl postgres::Host for OutboundPg {
103103
}
104104
}
105105

106-
const DB_NULL: Option<i32> = None;
107-
108106
fn to_sql_parameter(value: &ParameterValue) -> anyhow::Result<&(dyn ToSql + Sync)> {
109107
match value {
110108
ParameterValue::Boolean(v) => Ok(v),
@@ -120,7 +118,7 @@ fn to_sql_parameter(value: &ParameterValue) -> anyhow::Result<&(dyn ToSql + Sync
120118
| ParameterValue::Uint64(_) => Err(anyhow!("Postgres does not support unsigned integers")),
121119
ParameterValue::Str(v) => Ok(v),
122120
ParameterValue::Binary(v) => Ok(v),
123-
ParameterValue::DbNull => Ok(&DB_NULL),
121+
ParameterValue::DbNull => Ok(&PgNull),
124122
}
125123
}
126124

@@ -285,3 +283,45 @@ where
285283
}
286284
});
287285
}
286+
287+
/// Although the Postgres crate converts Rust Option::None to Postgres NULL,
288+
/// it enforces the type of the Option as it does so. (For example, trying to
289+
/// pass an Option::<i32>::None to a VARCHAR column fails conversion.) As we
290+
/// do not know expected column types, we instead use a "neutral" custom type
291+
/// which allows conversion to any type but always tells the Postgres crate to
292+
/// treat it as a SQL NULL.
293+
struct PgNull;
294+
295+
impl ToSql for PgNull {
296+
fn to_sql(
297+
&self,
298+
_ty: &Type,
299+
_out: &mut tokio_postgres::types::private::BytesMut,
300+
) -> Result<tokio_postgres::types::IsNull, Box<dyn std::error::Error + Sync + Send>>
301+
where
302+
Self: Sized,
303+
{
304+
Ok(tokio_postgres::types::IsNull::Yes)
305+
}
306+
307+
fn accepts(_ty: &Type) -> bool
308+
where
309+
Self: Sized,
310+
{
311+
true
312+
}
313+
314+
fn to_sql_checked(
315+
&self,
316+
_ty: &Type,
317+
_out: &mut tokio_postgres::types::private::BytesMut,
318+
) -> Result<tokio_postgres::types::IsNull, Box<dyn std::error::Error + Sync + Send>> {
319+
Ok(tokio_postgres::types::IsNull::Yes)
320+
}
321+
}
322+
323+
impl std::fmt::Debug for PgNull {
324+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
325+
f.debug_struct("NULL").finish()
326+
}
327+
}

0 commit comments

Comments
 (0)