Skip to content

Commit ea50336

Browse files
committed
feat: Implement custom handling for Option<T> in ODBC arguments
This commit adds a custom implementation for encoding and decoding Option<T> types in the ODBC arguments module. It enhances the handling of null values by allowing for proper encoding of optional parameters, ensuring compatibility with the ODBC API. Additionally, a new test is introduced to verify the binding of null string parameters in SQL queries.
1 parent 297eff2 commit ea50336

File tree

4 files changed

+59
-4
lines changed

4 files changed

+59
-4
lines changed

sqlx-core/src/odbc/arguments.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ pub struct OdbcArguments<'q> {
88
pub(crate) values: Vec<OdbcArgumentValue<'q>>,
99
}
1010

11+
#[derive(Debug, Clone)]
1112
pub enum OdbcArgumentValue<'q> {
1213
Text(String),
1314
Bytes(Vec<u8>),
@@ -116,3 +117,40 @@ impl<'q> Encode<'q, Odbc> for Vec<u8> {
116117
crate::encode::IsNull::No
117118
}
118119
}
120+
121+
impl<'q, T> Encode<'q, Odbc> for Option<T>
122+
where
123+
T: Encode<'q, Odbc> + Type<Odbc> + 'q,
124+
{
125+
fn produces(&self) -> Option<crate::odbc::OdbcTypeInfo> {
126+
if let Some(v) = self {
127+
v.produces()
128+
} else {
129+
T::type_info().into()
130+
}
131+
}
132+
133+
fn encode(self, buf: &mut Vec<OdbcArgumentValue<'q>>) -> crate::encode::IsNull {
134+
match self {
135+
Some(v) => v.encode(buf),
136+
None => {
137+
buf.push(OdbcArgumentValue::Null);
138+
crate::encode::IsNull::Yes
139+
}
140+
}
141+
}
142+
143+
fn encode_by_ref(&self, buf: &mut Vec<OdbcArgumentValue<'q>>) -> crate::encode::IsNull {
144+
match self {
145+
Some(v) => v.encode_by_ref(buf),
146+
None => {
147+
buf.push(OdbcArgumentValue::Null);
148+
crate::encode::IsNull::Yes
149+
}
150+
}
151+
}
152+
153+
fn size_hint(&self) -> usize {
154+
self.as_ref().map_or(0, Encode::size_hint)
155+
}
156+
}

sqlx-core/src/odbc/connection/worker.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ fn execute_sql_with_params(
265265

266266
let mut params: Vec<Box<dyn odbc_api::parameter::InputParameter>> =
267267
Vec::with_capacity(args.len());
268-
for a in args {
268+
for a in dbg!(args) {
269269
params.push(to_param(a));
270270
}
271271
dispatch_execute(conn, sql, &params[..], tx);
@@ -280,7 +280,7 @@ fn to_param(
280280
OdbcArgumentValue::Text(s) => Box::new(s.into_parameter()),
281281
OdbcArgumentValue::Bytes(b) => Box::new(b.into_parameter()),
282282
OdbcArgumentValue::Null | OdbcArgumentValue::Phantom(_) => {
283-
Box::new(Option::<i32>::None.into_parameter())
283+
Box::new(Option::<String>::None.into_parameter())
284284
}
285285
}
286286
}

sqlx-core/src/odbc/mod.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,5 +47,4 @@ impl_column_index_for_statement!(OdbcStatement);
4747
impl_acquire!(Odbc, OdbcConnection);
4848
impl_into_maybe_pool!(Odbc, OdbcConnection);
4949

50-
// required because some databases have a different handling of NULL
51-
impl_encode_for_option!(Odbc);
50+
// custom Option<..> handling implemented in `arguments.rs`

tests/odbc/odbc.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,3 +210,21 @@ async fn it_can_bind_heterogeneous_params() -> anyhow::Result<()> {
210210
assert_eq!(last, 42);
211211
Ok(())
212212
}
213+
214+
#[tokio::test]
215+
async fn it_binds_null_string_parameter() -> anyhow::Result<()> {
216+
let mut conn = new::<Odbc>().await?;
217+
let stmt = (&mut conn).prepare("SELECT ?, ?").await?;
218+
let row = stmt
219+
.query()
220+
.bind("abc")
221+
.bind(Option::<String>::None)
222+
.fetch_one(&mut conn)
223+
.await?;
224+
225+
let a = row.try_get_raw(0)?.to_owned().decode::<String>();
226+
let b = row.try_get_raw(1)?.to_owned();
227+
assert_eq!(a, "abc");
228+
assert!(b.is_null());
229+
Ok(())
230+
}

0 commit comments

Comments
 (0)