Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
094d43c
refactor(odbc): implement bulk fetch using columnar buffers
lovasoa Sep 25, 2025
36d1516
refactor(odbc): enhance OdbcValue and ColumnData structures
lovasoa Sep 25, 2025
4b5be2b
fix tests
lovasoa Sep 25, 2025
0ff092e
refactor(odbc): update text length constants and improve buffer handling
lovasoa Sep 25, 2025
c4b1b8f
refactor(odbc): improve binary data length handling in buffer descrip…
lovasoa Sep 25, 2025
0ff2953
feat(odbc): add support for raw ODBC Date, Time, and Timestamp values
lovasoa Sep 25, 2025
52fc57c
refactor(odbc): streamline error handling in date and time decoding
lovasoa Sep 25, 2025
3f7641a
Merge branch 'main' into odbc-fetch-batch
lovasoa Sep 29, 2025
247bc3d
snowflake test
lovasoa Sep 29, 2025
2ef1213
refactor(odbc): store nullability separately
lovasoa Sep 29, 2025
698cb00
test(odbc): enhance mixed null and values tests
lovasoa Sep 29, 2025
e9eb8d0
refactor(odbc): introduce OdbcBatch for reduced cloning
lovasoa Sep 29, 2025
9ba2926
refactor(odbc): change OdbcBatch columns to use Arc for improved memo…
lovasoa Sep 29, 2025
65829db
feat(odbc): introduce OdbcBufferSettings for performance tuning
lovasoa Sep 29, 2025
0a66a1b
refactor(odbc): fix tests
lovasoa Sep 29, 2025
6fa10ae
refactor(odbc): remove some code duplication
lovasoa Sep 29, 2025
fc43b6e
feat(odbc): implement unbuffered mode for ODBC connections
lovasoa Sep 29, 2025
94c2628
refactor(odbc): enhance OdbcBufferSettings and improve data fetching …
lovasoa Sep 29, 2025
1cf7da5
refactor(odbc): simplify OdbcValueVec usage and improve type handling
lovasoa Sep 29, 2025
40f7b38
feat(error): add MismatchedTypeError for improved type mismatch handling
lovasoa Sep 29, 2025
1498620
refactor(odbc): improve error handling and data fetching logic
lovasoa Sep 29, 2025
27e8062
refactor(postgres): fix tests
lovasoa Sep 29, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 0 additions & 16 deletions sqlx-core/src/any/error.rs

This file was deleted.

1 change: 0 additions & 1 deletion sqlx-core/src/any/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ mod arguments;
pub(crate) mod column;
mod connection;
mod database;
mod error;
mod kind;
mod options;
mod query_result;
Expand Down
4 changes: 2 additions & 2 deletions sqlx-core/src/any/row.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use crate::any::error::mismatched_types;
use crate::any::{Any, AnyColumn, AnyColumnIndex};
use crate::column::ColumnIndex;
use crate::database::HasValueRef;
use crate::decode::Decode;
use crate::error::mismatched_types;
use crate::error::Error;
use crate::row::Row;
use crate::type_info::TypeInfo;
Expand Down Expand Up @@ -91,7 +91,7 @@ impl Row for AnyRow {
let ty = value.type_info();

if !value.is_null() && !ty.is_null() && !T::compatible(&ty) {
Err(mismatched_types::<T>(&ty))
Err(mismatched_types::<Self::Database, T>(&ty))
} else {
T::decode(value)
}
Expand Down
10 changes: 6 additions & 4 deletions sqlx-core/src/any/value.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
use std::borrow::Cow;

use crate::any::error::mismatched_types;
use crate::any::{Any, AnyTypeInfo};
use crate::database::HasValueRef;
use crate::decode::Decode;
use crate::error::Error;
use crate::error::mismatched_types;
use crate::type_info::TypeInfo;
use crate::types::Type;
use crate::value::{Value, ValueRef};
Expand Down Expand Up @@ -113,15 +112,18 @@ impl Value for AnyValue {
}
}

fn try_decode<'r, T>(&'r self) -> Result<T, Error>
fn try_decode<'r, T>(&'r self) -> crate::error::Result<T>
where
T: Decode<'r, Self::Database> + Type<Self::Database>,
{
if !self.is_null() {
let ty = self.type_info();

if !ty.is_null() && !T::compatible(&ty) {
return Err(Error::Decode(mismatched_types::<T>(&ty)));
return Err(crate::error::Error::Decode(mismatched_types::<
Self::Database,
T,
>(&ty)));
}
}

Expand Down
56 changes: 48 additions & 8 deletions sqlx-core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,43 @@ pub type BoxDynError = Box<dyn StdError + 'static + Send + Sync>;
#[error("unexpected null; try decoding as an `Option`")]
pub struct UnexpectedNullError;

/// Error indicating that a Rust type is not compatible with a SQL type.
#[derive(thiserror::Error, Debug)]
#[error("mismatched types; Rust type `{rust_type}` (as SQL type `{rust_sql_type}`) could not be decoded into SQL type `{sql_type}`")]
pub struct MismatchedTypeError {
/// The name of the Rust type.
pub rust_type: String,
/// The SQL type name that the Rust type would map to.
pub rust_sql_type: String,
/// The actual SQL type from the database.
pub sql_type: String,
/// Optional source error that caused the mismatch.
#[source]
pub source: Option<BoxDynError>,
}

impl MismatchedTypeError {
/// Create a new mismatched type error without a source.
pub fn new<DB: Database, T: Type<DB>>(ty: &DB::TypeInfo) -> Self {
Self {
rust_type: type_name::<T>().to_string(),
rust_sql_type: T::type_info().name().to_string(),
sql_type: ty.name().to_string(),
source: None,
}
}

/// Create a new mismatched type error with a source error.
pub fn with_source<DB: Database, T: Type<DB>>(ty: &DB::TypeInfo, source: BoxDynError) -> Self {
Self {
rust_type: type_name::<T>().to_string(),
rust_sql_type: T::type_info().name().to_string(),
sql_type: ty.name().to_string(),
source: Some(source),
}
}
}

/// Represents all the ways a method can fail within SQLx.
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
Expand Down Expand Up @@ -145,14 +182,17 @@ impl Error {
}

pub(crate) fn mismatched_types<DB: Database, T: Type<DB>>(ty: &DB::TypeInfo) -> BoxDynError {
// TODO: `#name` only produces `TINYINT` but perhaps we want to show `TINYINT(1)`
format!(
"mismatched types; Rust type `{}` (as SQL type `{}`) is not compatible with SQL type `{}`",
type_name::<T>(),
T::type_info().name(),
ty.name()
)
.into()
Box::new(MismatchedTypeError {
rust_type: format!(
"{} ({}compatible with SQL type `{}`)",
type_name::<T>(),
if T::compatible(ty) { "" } else { "in" },
T::type_info().name()
),
rust_sql_type: T::type_info().name().to_string(),
sql_type: ty.name().to_string(),
source: None,
})
}

/// An error that was returned from the database.
Expand Down
9 changes: 7 additions & 2 deletions sqlx-core/src/odbc/connection/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use crate::connection::Connection;
use crate::error::Error;
use crate::odbc::{
Odbc, OdbcArguments, OdbcColumn, OdbcConnectOptions, OdbcQueryResult, OdbcRow, OdbcTypeInfo,
Odbc, OdbcArguments, OdbcBufferSettings, OdbcColumn, OdbcConnectOptions, OdbcQueryResult,
OdbcRow, OdbcTypeInfo,
};
use crate::transaction::Transaction;
use either::Either;
Expand Down Expand Up @@ -67,12 +68,14 @@ pub(super) fn decode_column_name<T: ColumnNameDecode>(name: T, index: u16) -> St
pub struct OdbcConnection {
pub(crate) conn: SharedConnection<'static>,
pub(crate) stmt_cache: HashMap<Arc<str>, SharedPreparedStatement>,
pub(crate) buffer_settings: OdbcBufferSettings,
}

impl std::fmt::Debug for OdbcConnection {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("OdbcConnection")
.field("conn", &self.conn)
.field("buffer_settings", &self.buffer_settings)
.finish()
}
}
Expand Down Expand Up @@ -108,6 +111,7 @@ impl OdbcConnection {
Ok(Self {
conn: shared_conn,
stmt_cache: HashMap::new(),
buffer_settings: options.buffer_settings,
})
}

Expand Down Expand Up @@ -162,9 +166,10 @@ impl OdbcConnection {
};

let conn = Arc::clone(&self.conn);
let buffer_settings = self.buffer_settings;
sqlx_rt::spawn(sqlx_rt::spawn_blocking(move || {
let mut conn = conn.lock().expect("failed to lock connection");
if let Err(e) = execute_sql(&mut conn, maybe_prepared, args, &tx) {
if let Err(e) = execute_sql(&mut conn, maybe_prepared, args, &tx, buffer_settings) {
let _ = tx.send(Err(e));
}
}));
Expand Down
Loading
Loading