diff --git a/sqlx-sqlite/src/connection/explain.rs b/sqlx-sqlite/src/connection/explain.rs index c9247b4ef2..d5c8c5e8ef 100644 --- a/sqlx-sqlite/src/connection/explain.rs +++ b/sqlx-sqlite/src/connection/explain.rs @@ -125,6 +125,7 @@ const OP_CONCAT: &str = "Concat"; const OP_OFFSET_LIMIT: &str = "OffsetLimit"; const OP_RESULT_ROW: &str = "ResultRow"; const OP_HALT: &str = "Halt"; +const OP_HALT_IF_NULL: &str = "HaltIfNull"; const MAX_LOOP_COUNT: u8 = 2; @@ -942,6 +943,16 @@ pub(super) fn explain( state.r.insert(p2, RegDataType::Single(ColumnType::null())); } + // if there is a value in p3, and the query passes, then + // we know that it is not nullable + OP_HALT_IF_NULL => { + if let Some(RegDataType::Single(ColumnType::Single { nullable, .. })) = + state.r.get_mut(&p3) + { + *nullable = Some(false); + } + } + OP_FUNCTION => { // r[p1] = func( _ ) match from_utf8(p4).map_err(Error::protocol)? { diff --git a/tests/sqlite/describe.rs b/tests/sqlite/describe.rs index 22121bbd1e..51fe58de0c 100644 --- a/tests/sqlite/describe.rs +++ b/tests/sqlite/describe.rs @@ -250,6 +250,22 @@ async fn it_describes_insert_with_returning() -> anyhow::Result<()> { Ok(()) } +#[sqlx_macros::test] +async fn it_describes_bound_columns_non_null() -> anyhow::Result<()> { + let mut conn = new::().await?; + let d = conn + .describe("INSERT INTO tweet (id, text) VALUES ($1, $2) returning *") + .await?; + + assert_eq!(d.columns().len(), 4); + assert_eq!(d.column(0).type_info().name(), "INTEGER"); + assert_eq!(d.nullable(0), Some(false)); + assert_eq!(d.column(1).type_info().name(), "TEXT"); + assert_eq!(d.nullable(1), Some(false)); + + Ok(()) +} + #[sqlx_macros::test] async fn it_describes_update_with_returning() -> anyhow::Result<()> { let mut conn = new::().await?;