From 9daad9a343d061d2b1c41d8a21fd85d85acb981f Mon Sep 17 00:00:00 2001 From: Joel Parker Henderson Date: Tue, 1 Sep 2026 19:33:21 +0100 Subject: [PATCH] Replace tiberius with mssql (maintained fork with security fixes) tiberius currently has three unpatched RUSTSEC advisories (RUSTSEC-2026-0098, -0099, -0104) in its rustls-webpki dependency, a fix PR (prisma/tiberius#419) open since 2026-05-12 with no maintainer response, 11 reachable panic sites from untrusted server input (prisma/tiberius#424, #425), and a maintainer handover request (prisma/tiberius#427) unanswered for 11+ days. Renames the src_tiberius feature and tiberius module to src_mssql / mssql, and swaps the dependency to mssql (https://crates.io/crates/mssql, https://github.com/mssql-rust/mssql-rust), a fork that keeps the same Client/ColumnData/ToSql/etc. API tiberius had. Mechanical rename across: connector_arrow/Cargo.toml - dependency + feature rename connector_arrow/src/lib.rs - module declaration connector_arrow/src/errors.rs - error conversion variant connector_arrow/src/tiberius/* - renamed to src/mssql/*, types renamed connector_arrow/tests/it/* - renamed to match, TIBERIUS_URL -> MSSQL_URL Justfile, README.md - env var and support-matrix updates Two comments explicitly note behavior inherited from tiberius (a decimal precision restriction, and a lifetime-transmute workaround) rather than silently relabeling them, since I haven't independently verified whether the mssql fork has fixed either. Verified against the published mssql 1.0.1 crate: cargo check --features all cargo check --features all --tests Both pass. This is a breaking change: src_tiberius is renamed to src_mssql, and the connector_arrow::tiberius module/types are now connector_arrow::mssql. The crate is pre-1.0 (0.12.1), so this can ship as a minor version bump. An equally-maintained alternative fork, tiberius-ng (https://github.com/MattJackson/tiberius-ng), also fixes these issues and keeps the tiberius name/API if you'd prefer that direction instead. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01HmakCYmBF3qUgiW7ctoFJm --- Justfile | 2 +- README.md | 4 +- connector_arrow/Cargo.toml | 10 ++-- connector_arrow/src/errors.rs | 4 +- connector_arrow/src/lib.rs | 4 +- .../src/{tiberius => mssql}/append.rs | 12 ++-- .../src/{tiberius => mssql}/mod.rs | 29 ++++----- .../src/{tiberius => mssql}/query.rs | 60 +++++++++---------- .../src/{tiberius => mssql}/schema.rs | 10 ++-- .../src/{tiberius => mssql}/types.rs | 4 +- connector_arrow/tests/it/main.rs | 4 +- .../it/{test_tiberius.rs => test_mssql.rs} | 10 ++-- 12 files changed, 78 insertions(+), 75 deletions(-) rename connector_arrow/src/{tiberius => mssql}/append.rs (94%) rename connector_arrow/src/{tiberius => mssql}/mod.rs (85%) rename connector_arrow/src/{tiberius => mssql}/query.rs (80%) rename connector_arrow/src/{tiberius => mssql}/schema.rs (93%) rename connector_arrow/src/{tiberius => mssql}/types.rs (94%) rename connector_arrow/tests/it/{test_tiberius.rs => test_mssql.rs} (98%) diff --git a/Justfile b/Justfile index f1aef2b..7fe1a2a 100644 --- a/Justfile +++ b/Justfile @@ -1,6 +1,6 @@ export POSTGRES_URL := "postgres://user:pass@localhost:5432/db" export MYSQL_URL := "mysql://root:pass@localhost:3306/db" -export TIBERIUS_URL := "tds://sa:passwordA1@localhost:1433" +export MSSQL_URL := "tds://sa:passwordA1@localhost:1433" default: just --list diff --git a/README.md b/README.md index 31a4c10..94b0a12 100644 --- a/README.md +++ b/README.md @@ -32,8 +32,8 @@ without need for dynamic linking of C libraries. | RDBMS | SQLite | DuckDB | PostgreSQL | MySQL | Microsoft SQL Server | | --- | --- | --- | --- | --- | --- | -| feature | `src_rusqlite` | `src_duckdb` | `src_postgres` | `src_mysql` | `src_tiberius` | -| dependency | [rusqlite](https://crates.io/crates/rusqlite) | [duckdb](https://crates.io/crates/duckdb) | [postgres](https://crates.io/crates/postgres) | [mysql](https://crates.io/crates/mysql) | [tiberius](https://crates.io/crates/tiberius) | +| feature | `src_rusqlite` | `src_duckdb` | `src_postgres` | `src_mysql` | `src_mssql` | +| dependency | [rusqlite](https://crates.io/crates/rusqlite) | [duckdb](https://crates.io/crates/duckdb) | [postgres](https://crates.io/crates/postgres) | [mysql](https://crates.io/crates/mysql) | [mssql](https://crates.io/crates/mssql) (fork of [tiberius](https://crates.io/crates/tiberius)) | | query | x | x | x | x | x | | query params | x | x | x | | x | | schema get | x | x | x | x | x | diff --git a/connector_arrow/Cargo.toml b/connector_arrow/Cargo.toml index d410f9a..8b79689 100644 --- a/connector_arrow/Cargo.toml +++ b/connector_arrow/Cargo.toml @@ -69,8 +69,10 @@ default-features = false optional = true features = ["minimal"] -[dependencies.tiberius] -version = "0.12.2" +[dependencies.mssql] +# https://crates.io/crates/mssql - a maintained fork of tiberius with the same +# Client/ColumnData/ToSql/etc. API this module already used from `tiberius`. +version = "1.0.1" default-features = false optional = true @@ -91,7 +93,7 @@ all = [ "src_duckdb", "src_postgres", "src_mysql", - "src_tiberius", + "src_mssql", ] src_postgres = [ "postgres", @@ -105,7 +107,7 @@ src_postgres = [ src_rusqlite = ["rusqlite"] src_duckdb = ["duckdb", "fallible-streaming-iterator"] src_mysql = ["mysql", "pac_cell"] -src_tiberius = ["tiberius", "tokio", "tokio-util", "futures"] +src_mssql = ["mssql", "tokio", "tokio-util", "futures"] [package.metadata.docs.rs] features = ["all"] diff --git a/connector_arrow/src/errors.rs b/connector_arrow/src/errors.rs index 9b66f91..2740537 100644 --- a/connector_arrow/src/errors.rs +++ b/connector_arrow/src/errors.rs @@ -55,9 +55,9 @@ pub enum ConnectorError { #[error(transparent)] MySQL(#[from] mysql::Error), - #[cfg(feature = "src_tiberius")] + #[cfg(feature = "src_mssql")] #[error(transparent)] - Tiberius(#[from] tiberius::error::Error), + Mssql(#[from] mssql::error::Error), } #[derive(Error, Debug)] diff --git a/connector_arrow/src/lib.rs b/connector_arrow/src/lib.rs index bf86141..7985653 100644 --- a/connector_arrow/src/lib.rs +++ b/connector_arrow/src/lib.rs @@ -68,8 +68,8 @@ pub mod mysql; pub mod postgres; #[cfg(feature = "src_rusqlite")] pub mod rusqlite; -#[cfg(feature = "src_tiberius")] -pub mod tiberius; +#[cfg(feature = "src_mssql")] +pub mod mssql; pub use arrow; pub use errors::*; diff --git a/connector_arrow/src/tiberius/append.rs b/connector_arrow/src/mssql/append.rs similarity index 94% rename from connector_arrow/src/tiberius/append.rs rename to connector_arrow/src/mssql/append.rs index 86b3df6..be01e2b 100644 --- a/connector_arrow/src/tiberius/append.rs +++ b/connector_arrow/src/mssql/append.rs @@ -5,8 +5,8 @@ use arrow::datatypes::*; use arrow::record_batch::RecordBatch; use futures::{AsyncRead, AsyncWrite}; use itertools::{zip_eq, Itertools}; -use tiberius::numeric::Numeric; -use tiberius::{BulkLoadRequest, Client, ColumnData, TokenRow}; +use mssql::numeric::Numeric; +use mssql::{BulkLoadRequest, Client, ColumnData, TokenRow}; use tokio::runtime::Runtime; use crate::api::Append; @@ -16,12 +16,12 @@ use crate::util::transport::{Consume, ConsumeTy}; use crate::util::ArrayCellRef; use crate::{impl_consume_unsupported, ConnectorError}; -pub struct TiberiusAppender<'c, S: AsyncRead + AsyncWrite + Unpin + Send> { +pub struct MssqlAppender<'c, S: AsyncRead + AsyncWrite + Unpin + Send> { rt: Arc, bulk_load: BulkLoadRequest<'c, S>, } -impl<'conn, S: AsyncRead + AsyncWrite + Unpin + Send> TiberiusAppender<'conn, S> { +impl<'conn, S: AsyncRead + AsyncWrite + Unpin + Send> MssqlAppender<'conn, S> { pub fn new( rt: Arc, client: &'conn mut Client, @@ -29,7 +29,7 @@ impl<'conn, S: AsyncRead + AsyncWrite + Unpin + Send> TiberiusAppender<'conn, S> ) -> Result { let table_name = escaped_ident(table_name).to_string(); - // Tiberius requires table_name to be 'conn, but does not really use it as such. + // mssql (inherited from tiberius) requires table_name to be 'conn, but does not really use it as such. // We convert our '_ into 'conn here. let table_name: &'conn str = unsafe { std::mem::transmute::<_, _>(table_name.as_str()) }; @@ -40,7 +40,7 @@ impl<'conn, S: AsyncRead + AsyncWrite + Unpin + Send> TiberiusAppender<'conn, S> } } -impl<'conn, S: AsyncRead + AsyncWrite + Unpin + Send> Append<'conn> for TiberiusAppender<'conn, S> { +impl<'conn, S: AsyncRead + AsyncWrite + Unpin + Send> Append<'conn> for MssqlAppender<'conn, S> { fn append(&mut self, batch: RecordBatch) -> Result<(), ConnectorError> { let schema = batch.schema(); let mut row_ref = zip_eq(batch.columns(), schema.fields()) diff --git a/connector_arrow/src/tiberius/mod.rs b/connector_arrow/src/mssql/mod.rs similarity index 85% rename from connector_arrow/src/tiberius/mod.rs rename to connector_arrow/src/mssql/mod.rs index 195fadd..50a917e 100644 --- a/connector_arrow/src/tiberius/mod.rs +++ b/connector_arrow/src/mssql/mod.rs @@ -3,7 +3,7 @@ mod query; mod schema; mod types; -pub use tiberius; +pub use mssql; use arrow::datatypes::*; use futures::{AsyncRead, AsyncWrite}; @@ -14,45 +14,45 @@ use tokio::runtime::Runtime; use crate::api::Connector; use crate::ConnectorError; -pub struct TiberiusConnection { +pub struct MssqlConnection { rt: Arc, - client: tiberius::Client, + client: mssql::Client, } -impl TiberiusConnection { - pub fn new(rt: Arc, client: tiberius::Client) -> Self { - TiberiusConnection { rt, client } +impl MssqlConnection { + pub fn new(rt: Arc, client: mssql::Client) -> Self { + MssqlConnection { rt, client } } - pub fn unwrap(self) -> (Arc, tiberius::Client) { + pub fn unwrap(self) -> (Arc, mssql::Client) { (self.rt, self.client) } - pub fn inner_mut(&mut self) -> (&mut Arc, &mut tiberius::Client) { + pub fn inner_mut(&mut self) -> (&mut Arc, &mut mssql::Client) { (&mut self.rt, &mut self.client) } } -impl Connector for TiberiusConnection { +impl Connector for MssqlConnection { type Stmt<'conn> - = query::TiberiusStatement<'conn, S> + = query::MssqlStatement<'conn, S> where Self: 'conn; type Append<'conn> - = append::TiberiusAppender<'conn, S> + = append::MssqlAppender<'conn, S> where Self: 'conn; fn query<'a>(&'a mut self, query: &str) -> Result, ConnectorError> { - Ok(query::TiberiusStatement { + Ok(query::MssqlStatement { conn: self, query: query.to_string(), }) } fn append<'a>(&'a mut self, table_name: &str) -> Result, ConnectorError> { - append::TiberiusAppender::new(self.rt.clone(), &mut self.client, table_name) + append::MssqlAppender::new(self.rt.clone(), &mut self.client, table_name) } #[allow(clippy::get_first)] @@ -160,7 +160,8 @@ impl Connector for TiberiusConnection< } fn can_decimal_fit_in_numeric(precision: u8, scale: i8) -> bool { - // TODO: this should be p <= 38, not p < 38. This restriction is a bug in tiberius. + // TODO: this should be p <= 38, not p < 38. This restriction is a bug inherited from tiberius; + // unclear whether the mssql fork has fixed it independently. precision < 38 && scale >= 0 && precision >= scale as u8 } diff --git a/connector_arrow/src/tiberius/query.rs b/connector_arrow/src/mssql/query.rs similarity index 80% rename from connector_arrow/src/tiberius/query.rs rename to connector_arrow/src/mssql/query.rs index 1ea643b..8cedb3e 100644 --- a/connector_arrow/src/tiberius/query.rs +++ b/connector_arrow/src/mssql/query.rs @@ -2,7 +2,7 @@ use arrow::{datatypes::*, record_batch::RecordBatch}; use futures::{AsyncRead, AsyncWrite, StreamExt}; use itertools::Itertools; use std::sync::Arc; -use tiberius::{ColumnData, QueryStream, ToSql}; +use mssql::{ColumnData, QueryStream, ToSql}; use tokio::runtime::Runtime; use crate::api::{ResultReader, Statement}; @@ -13,16 +13,16 @@ use crate::util::ArrayCellRef; use crate::util::{self, transport::Produce}; use crate::ConnectorError; -pub struct TiberiusStatement<'conn, S: AsyncRead + AsyncWrite + Unpin + Send> { - pub(super) conn: &'conn mut super::TiberiusConnection, +pub struct MssqlStatement<'conn, S: AsyncRead + AsyncWrite + Unpin + Send> { + pub(super) conn: &'conn mut super::MssqlConnection, pub(super) query: String, } impl<'conn, S: AsyncRead + AsyncWrite + Unpin + Send> Statement<'conn> - for TiberiusStatement<'conn, S> + for MssqlStatement<'conn, S> { type Reader<'stmt> - = TiberiusResultReader<'stmt> + = MssqlResultReader<'stmt> where Self: 'stmt; @@ -50,9 +50,9 @@ impl<'conn, S: AsyncRead + AsyncWrite + Unpin + Send> Statement<'conn> let schema = super::types::get_result_schema(columns)?; self.conn.rt.block_on(stream.next()); - Ok(TiberiusResultReader { + Ok(MssqlResultReader { schema, - stream: TiberiusStream { + stream: MssqlStream { rt: self.conn.rt.clone(), stream, }, @@ -60,23 +60,23 @@ impl<'conn, S: AsyncRead + AsyncWrite + Unpin + Send> Statement<'conn> } } -pub struct TiberiusResultReader<'stmt> { +pub struct MssqlResultReader<'stmt> { schema: SchemaRef, - stream: TiberiusStream<'stmt>, + stream: MssqlStream<'stmt>, } -struct TiberiusStream<'stmt> { +struct MssqlStream<'stmt> { rt: Arc, stream: QueryStream<'stmt>, } -impl<'stmt> ResultReader<'stmt> for TiberiusResultReader<'stmt> { +impl<'stmt> ResultReader<'stmt> for MssqlResultReader<'stmt> { fn get_schema(&mut self) -> Result { Ok(self.schema.clone()) } } -impl Iterator for TiberiusResultReader<'_> { +impl Iterator for MssqlResultReader<'_> { type Item = Result; fn next(&mut self) -> Option { @@ -84,9 +84,9 @@ impl Iterator for TiberiusResultReader<'_> { } } -impl<'s> util::RowsReader<'s> for TiberiusStream<'s> { +impl<'s> util::RowsReader<'s> for MssqlStream<'s> { type CellReader<'row> - = TiberiusCellReader + = MssqlCellReader where Self: 'row; @@ -98,30 +98,30 @@ impl<'s> util::RowsReader<'s> for TiberiusStream<'s> { // are there more result sets? let row = match item? { - tiberius::QueryItem::Row(row) => row, - tiberius::QueryItem::Metadata(_) => { + mssql::QueryItem::Row(row) => row, + mssql::QueryItem::Metadata(_) => { // yes, this there are return Err(ConnectorError::MultipleResultSets); } }; - Ok(Some(TiberiusCellReader { row, cell: 0 })) + Ok(Some(MssqlCellReader { row, cell: 0 })) } } -struct TiberiusCellReader { - row: tiberius::Row, +struct MssqlCellReader { + row: mssql::Row, cell: usize, } -impl util::CellReader<'_> for TiberiusCellReader { +impl util::CellReader<'_> for MssqlCellReader { type CellRef<'cell> - = TiberiusCellRef<'cell> + = MssqlCellRef<'cell> where Self: 'cell; fn next_cell(&mut self) -> Option> { - let r = TiberiusCellRef { + let r = MssqlCellRef { row: &mut self.row, cell: self.cell, }; @@ -131,12 +131,12 @@ impl util::CellReader<'_> for TiberiusCellReader { } #[derive(Debug)] -struct TiberiusCellRef<'a> { - row: &'a mut tiberius::Row, +struct MssqlCellRef<'a> { + row: &'a mut mssql::Row, cell: usize, } -impl<'r> Produce<'r> for TiberiusCellRef<'r> {} +impl<'r> Produce<'r> for MssqlCellRef<'r> {} macro_rules! impl_produce_ty { ($ArrTy: ty, $DbTy: ty) => { @@ -144,7 +144,7 @@ macro_rules! impl_produce_ty { }; ($ArrTy: ty, $DbTy: ty, $conversion: expr) => { - impl<'r> ProduceTy<'r, $ArrTy> for TiberiusCellRef<'r> { + impl<'r> ProduceTy<'r, $ArrTy> for MssqlCellRef<'r> { fn produce(self) -> Result<<$ArrTy as ArrowType>::Native, ConnectorError> { Ok(self .row @@ -170,7 +170,7 @@ impl_produce_ty!(Utf8Type, StrOrNum, StrOrNum::into_inner); impl_produce_ty!(LargeUtf8Type, &str, &str::to_owned); impl_produce_unsupported!( - TiberiusCellRef<'r>, + MssqlCellRef<'r>, ( NullType, Int8Type, @@ -211,8 +211,8 @@ impl StrOrNum { } } -impl<'a> tiberius::FromSql<'a> for StrOrNum { - fn from_sql(value: &'a ColumnData<'static>) -> tiberius::Result> { +impl<'a> mssql::FromSql<'a> for StrOrNum { + fn from_sql(value: &'a ColumnData<'static>) -> mssql::Result> { match value { ColumnData::String(s) => Ok(s.as_ref().map(|x| StrOrNum(x.to_string()))), ColumnData::Numeric(n) => Ok(n.as_ref().map(|x| { @@ -228,7 +228,7 @@ impl<'a> tiberius::FromSql<'a> for StrOrNum { StrOrNum(format!("{}", x.value())) } })), - _ => Err(tiberius::error::Error::Conversion( + _ => Err(mssql::error::Error::Conversion( format!("cannot convert `{value:?}` into string").into(), )), } diff --git a/connector_arrow/src/tiberius/schema.rs b/connector_arrow/src/mssql/schema.rs similarity index 93% rename from connector_arrow/src/tiberius/schema.rs rename to connector_arrow/src/mssql/schema.rs index 359366a..b503e9e 100644 --- a/connector_arrow/src/tiberius/schema.rs +++ b/connector_arrow/src/mssql/schema.rs @@ -9,7 +9,7 @@ use crate::api::{Connector, SchemaEdit, SchemaGet}; use crate::util::escape::escaped_ident; use crate::{ConnectorError, TableCreateError, TableDropError}; -impl SchemaGet for super::TiberiusConnection { +impl SchemaGet for super::MssqlConnection { fn table_list(&mut self) -> Result, ConnectorError> { let query = " SELECT TABLE_NAME @@ -47,7 +47,7 @@ impl SchemaGet for super::TiberiusConn TABLE_NAME = @P1 ORDER BY ORDINAL_POSITION; "; - let params: [&dyn tiberius::ToSql; 1] = [&table_name.to_string()]; + let params: [&dyn mssql::ToSql; 1] = [&table_name.to_string()]; let res = self.client.query(query, ¶ms); let res = self.rt.block_on(res)?; @@ -76,7 +76,7 @@ impl SchemaGet for super::TiberiusConn } } -impl SchemaEdit for super::TiberiusConnection { +impl SchemaEdit for super::MssqlConnection { fn table_create(&mut self, name: &str, schema: SchemaRef) -> Result<(), TableCreateError> { let column_defs = schema .fields() @@ -102,7 +102,7 @@ impl SchemaEdit for super::TiberiusCon match res { Ok(_) => Ok(()), - Err(tiberius::error::Error::Server(e)) if e.code() == 2714 => { + Err(mssql::error::Error::Server(e)) if e.code() == 2714 => { Err(TableCreateError::TableExists) } Err(e) => Err(TableCreateError::Connector(e.into())), @@ -116,7 +116,7 @@ impl SchemaEdit for super::TiberiusCon match res { Ok(_) => Ok(()), - Err(tiberius::error::Error::Server(e)) if e.code() == 3701 => { + Err(mssql::error::Error::Server(e)) if e.code() == 3701 => { Err(TableDropError::TableNonexistent) } Err(e) => Err(TableDropError::Connector(e.into())), diff --git a/connector_arrow/src/tiberius/types.rs b/connector_arrow/src/mssql/types.rs similarity index 94% rename from connector_arrow/src/tiberius/types.rs rename to connector_arrow/src/mssql/types.rs index 038cb64..af38512 100644 --- a/connector_arrow/src/tiberius/types.rs +++ b/connector_arrow/src/mssql/types.rs @@ -1,7 +1,7 @@ use std::{collections::HashMap, sync::Arc}; use arrow::datatypes::*; -use tiberius::{Column, ColumnType}; +use mssql::{Column, ColumnType}; use tokio::net::TcpStream; use tokio_util::compat::Compat; @@ -25,7 +25,7 @@ pub fn get_result_schema(columns: Option<&[Column]>) -> Result Field { let mut metadata = HashMap::new(); - let data_type = super::TiberiusConnection::>::type_db_into_arrow(db_ty); + let data_type = super::MssqlConnection::>::type_db_into_arrow(db_ty); // if we cannot map to an arrow type, map into a binary let data_type = data_type.unwrap_or_else(|| { diff --git a/connector_arrow/tests/it/main.rs b/connector_arrow/tests/it/main.rs index 4f2f984..4931542 100644 --- a/connector_arrow/tests/it/main.rs +++ b/connector_arrow/tests/it/main.rs @@ -11,5 +11,5 @@ mod test_mysql; mod test_postgres; #[cfg(feature = "src_rusqlite")] mod test_sqlite; -#[cfg(feature = "src_tiberius")] -mod test_tiberius; +#[cfg(feature = "src_mssql")] +mod test_mssql; diff --git a/connector_arrow/tests/it/test_tiberius.rs b/connector_arrow/tests/it/test_mssql.rs similarity index 98% rename from connector_arrow/tests/it/test_tiberius.rs rename to connector_arrow/tests/it/test_mssql.rs index f15b6a0..ee1c77f 100644 --- a/connector_arrow/tests/it/test_tiberius.rs +++ b/connector_arrow/tests/it/test_mssql.rs @@ -1,14 +1,14 @@ use std::sync::Arc; -use connector_arrow::tiberius::TiberiusConnection; +use connector_arrow::mssql::MssqlConnection; use rstest::rstest; -use tiberius::{AuthMethod, Client, Config}; +use mssql::{AuthMethod, Client, Config}; use tokio::{net::TcpStream, runtime}; use tokio_util::compat::{Compat, TokioAsyncWriteCompatExt}; use crate::{spec, util::QueryOfSingleLiteral}; -fn init() -> TiberiusConnection> { +fn init() -> MssqlConnection> { let _ = env_logger::builder().is_test(true).try_init(); let rt = Arc::new( @@ -18,7 +18,7 @@ fn init() -> TiberiusConnection> { .unwrap(), ); - let url = std::env::var("TIBERIUS_URL").unwrap(); + let url = std::env::var("MSSQL_URL").unwrap(); let url = url::Url::parse(&url).unwrap(); let mut config = Config::new(); @@ -36,7 +36,7 @@ fn init() -> TiberiusConnection> { let client = Client::connect(config, tcp.compat_write()); let client = rt.block_on(client).unwrap(); - TiberiusConnection::new(rt, client) + MssqlConnection::new(rt, client) } #[test]