Skip to main content

sqlx_mssql_odbc/
lib.rs

1//! MSSQL driver for SQLx via ODBC.
2//!
3//! `sqlx-mssql-odbc` connects SQLx to Microsoft SQL Server through an ODBC driver
4//! manager. This is the **facade crate** — it re-exports everything from
5//! [`sqlx-mssql-odbc-core`].
6//!
7//! # Quick start
8//!
9//! ```toml
10//! [dependencies]
11//! sqlx-core = "=0.9.0"
12//! sqlx-mssql-odbc = "0.1"
13//! tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
14//! ```
15//!
16//! ```no_run
17//! use sqlx_core::connection::Connection;
18//! use sqlx_core::row::Row;
19//! use sqlx_mssql_odbc::MssqlConnection;
20//!
21//! # async fn run() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
22//! let mut conn = MssqlConnection::connect(
23//!     "mssql://user:password@localhost:1433/database"
24//! ).await?;
25//!
26//! let row = sqlx_core::query::query("SELECT 42")
27//!     .fetch_one(&mut conn)
28//!     .await?;
29//!
30//! let value: i32 = row.try_get(0)?;
31//! assert_eq!(value, 42);
32//!
33//! conn.close().await?;
34//! # Ok(())
35//! # }
36//! ```
37//!
38//! `MssqlConnection::connect()` accepts a standard `mssql://` URL, a raw ODBC
39//! connection string, or a bare DSN name.
40//!
41//! # Usage patterns
42//!
43//! ## Connection pooling
44//!
45//! ```no_run
46//! use sqlx_mssql_odbc::MssqlPoolOptions;
47//!
48//! # async fn run() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
49//! let pool = MssqlPoolOptions::new()
50//!     .max_connections(10)
51//!     .connect("mssql://user:password@localhost:1433/database")
52//!     .await?;
53//!
54//! let row = sqlx_core::query::query("SELECT 1")
55//!     .fetch_one(&pool)
56//!     .await?;
57//!
58//! pool.close().await;
59//! # Ok(())
60//! # }
61//! ```
62//!
63//! ## Parameterised queries
64//!
65//! ```no_run
66//! use sqlx_core::row::Row;
67//! use sqlx_mssql_odbc::MssqlConnection;
68//! use sqlx_core::connection::Connection;
69//!
70//! # async fn run() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
71//! let mut conn = MssqlConnection::connect("mssql://…").await?;
72//!
73//! let row = sqlx_core::query::query("SELECT @p1 + @p2")
74//!     .bind(10i32)
75//!     .bind(20i32)
76//!     .fetch_one(&mut conn)
77//!     .await?;
78//!
79//! let total: i32 = row.try_get(0)?;
80//! assert_eq!(total, 30);
81//! # Ok(())
82//! # }
83//! ```
84//!
85//! ## Compile-time checked queries (`macros` feature)
86//!
87//! Enable the `macros` feature to get compile-time validation of SQL against a
88//! live database:
89//!
90//! ```toml
91//! [dependencies]
92//! sqlx-mssql-odbc = { version = "0.1", features = ["macros"] }
93//! ```
94//!
95//! ```ignore
96//! # use sqlx_mssql_odbc::MssqlConnection;
97//! # use sqlx_core::connection::Connection;
98//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
99//! let mut conn = MssqlConnection::connect("…").await?;
100//!
101//! // The column name and type are checked at compile time.
102//! let row = sqlx_mssql_odbc::query!("SELECT 1 AS one")
103//!     .fetch_one(&mut conn)
104//!     .await?;
105//! assert_eq!(row.one, 1i32);
106//!
107//! // Bind parameters with $1, $2, etc.
108//! let row = sqlx_mssql_odbc::query!("SELECT @p1 + @p2 AS total", 10i32, 20i32)
109//!     .fetch_one(&mut conn)
110//!     .await?;
111//! assert_eq!(row.total, 30);
112//! # Ok(())
113//! # }
114//! ```
115//!
116//! For CI or offline builds, use `cargo sqlx prepare` to cache the schema so
117//! the macros can check queries without a live database.
118//!
119//! ## Derive macros (`derive` feature)
120//!
121//! ```toml
122//! [dependencies]
123//! sqlx-mssql-odbc = { version = "0.1", features = ["derive"] }
124//! ```
125//!
126//! ```ignore
127//! use sqlx_mssql_odbc::FromRow;
128//!
129//! #[derive(Debug, FromRow)]
130//! struct User {
131//!     id: i32,
132//!     name: String,
133//!     email: Option<String>,
134//! }
135//!
136//! # async fn example(mut conn: sqlx_mssql_odbc::MssqlConnection)
137//! #     -> Result<(), Box<dyn std::error::Error>>
138//! # {
139//! let users = sqlx_mssql_odbc::query_as!(User, "SELECT id, name, email FROM users")
140//!     .fetch_all(&mut conn)
141//!     .await?;
142//! # Ok(())
143//! # }
144//! ```
145//!
146//! ## Transactions
147//!
148//! ```no_run
149//! use sqlx_core::connection::Connection;
150//! use sqlx_core::executor::Executor;
151//! use sqlx_mssql_odbc::MssqlConnection;
152//!
153//! # async fn run() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
154//! let mut conn = MssqlConnection::connect("mssql://…").await?;
155//!
156//! conn.begin().await?;
157//! sqlx_core::query::query("INSERT INTO users (name) VALUES (@p1)")
158//!     .bind("Alice")
159//!     .execute(&mut conn)
160//!     .await?;
161//! conn.commit().await?;
162//! # Ok(())
163//! # }
164//! ```
165//!
166//! # URL parameters
167//!
168//! | Parameter | Description |
169//! |---|---|
170//! | `encrypt=true` | Enable TLS encryption |
171//! | `trust_certificate=true` | Skip certificate validation |
172//! | `driver=…` | Custom ODBC driver name (default: `ODBC Driver 18 for SQL Server`) |
173//!
174//! # Requirements
175//!
176//! On Linux and macOS you need both a driver manager (unixODBC) and the
177//! Microsoft ODBC Driver for SQL Server (version 17 or 18). See the
178//! [repository README](https://github.com/strawberyy-coconut/sqlx-mssql-odbc)
179//! for platform-specific installation instructions.
180//!
181//! Enable the `vendored-unix-odbc` feature to statically link unixODBC into
182//! your application on Linux or macOS.
183//!
184//! # Features
185//!
186//! | Feature | Description |
187//! |---|---|
188//! | `bigdecimal` | [`BigDecimal`] type support |
189//! | `chrono` | [`chrono`] datetime types |
190//! | `rust_decimal` / `decimal` | [`rust_decimal::Decimal`] support |
191//! | `json` | [`serde_json::Value`] support |
192//! | `time` | [`time`] crate datetime types |
193//! | `uuid` | [`uuid::Uuid`] support |
194//! | `macros` | `query!()`, `query_as!()` and other proc macros |
195//! | `derive` | `Encode`, `Decode`, `Type`, `FromRow` derive macros |
196//! | `offline` | Compile-time query checking with `query!()` |
197//! | `migrate` | Database migration support |
198//! | `runtime-tokio` | Tokio runtime support |
199//! | `tls-none` | No TLS (default) |
200//! | `spatial` | [`geo_types`] spatial type support |
201//! | `vendored-unix-odbc` | Statically link unixODBC |
202
203#![cfg_attr(docsrs, feature(doc_cfg))]
204
205// Re-export everything from sqlx-mssql-odbc-core.
206pub use sqlx_mssql_odbc_core::*;
207
208/// Re-export of the core crate for direct access.
209pub use sqlx_mssql_odbc_core as core;
210
211// ---------------------------------------------------------------------------
212// Macro wrappers — only available with the `macros` feature
213// ---------------------------------------------------------------------------
214
215#[cfg(feature = "macros")]
216#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
217pub use sqlx_mssql_odbc_macros::expand_query;
218
219/// Compile-time checked SQL query for MSSQL via ODBC.
220///
221/// The SQL is sent to a live database at compile time so column names and
222/// types are verified. The returned row lets you access columns as named
223/// fields with correct Rust types — no runtime mapping needed.
224///
225/// # Without parameters
226///
227/// ```ignore
228/// # use sqlx_mssql_odbc::MssqlConnection;
229/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
230/// let mut conn = MssqlConnection::connect("…").await?;
231///
232/// let row = sqlx_mssql_odbc::query!("SELECT id, name, email FROM users WHERE id = 1")
233///     .fetch_one(&mut conn)
234///     .await?;
235///
236/// // Fields are checked at compile time — typos become compile errors!
237/// println!("{} <{}>", row.name, row.email.unwrap_or_default());
238/// # Ok(())
239/// # }
240/// ```
241///
242/// # With parameters
243///
244/// Bind parameters with `@p1`, `@p2`, etc. The macro infers types from the
245/// Rust expressions:
246///
247/// ```ignore
248/// # use sqlx_mssql_odbc::MssqlConnection;
249/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
250/// # let mut conn = MssqlConnection::connect("…").await?;
251/// let user_id = 42;
252/// let rows = sqlx_mssql_odbc::query!(
253///     "SELECT id, name FROM users WHERE id = @p1 OR name LIKE @p2",
254///     user_id,
255///     "%Smith%"
256/// )
257///     .fetch_all(&mut conn)
258///     .await?;
259/// # Ok(())
260/// # }
261/// ```
262///
263/// # Inferring multiple result columns
264///
265/// ```ignore
266/// # use sqlx_mssql_odbc::MssqlConnection;
267/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
268/// # let mut conn = MssqlConnection::connect("…").await?;
269/// let row = sqlx_mssql_odbc::query!("SELECT COUNT(*) AS count, AVG(age) AS avg_age FROM users")
270///     .fetch_one(&mut conn)
271///     .await?;
272/// println!("{} users, average age {}", row.count, row.avg_age);
273/// # Ok(())
274/// # }
275/// ```
276///
277/// Requires the `macros` feature and a live database (or a prepared offline
278/// cache via `cargo sqlx prepare`).
279#[cfg(feature = "macros")]
280#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
281#[macro_export]
282macro_rules! query {
283    ($query:expr) => ({
284        $crate::expand_query!(source = $query)
285    });
286    ($query:expr, $($args:tt)*) => ({
287        $crate::expand_query!(source = $query, args = [$($args)*])
288    });
289}
290
291/// Compile-time checked SQL query for MSSQL via ODBC, mapping to a named struct.
292///
293/// Like [`query!`] but deserialises each row into a struct you provide. The
294/// struct fields are matched to columns by name and their types are checked
295/// against the database schema at compile time.
296///
297/// ```ignore
298/// # use sqlx_mssql_odbc::MssqlConnection;
299/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
300/// # let mut conn = MssqlConnection::connect("…").await?;
301/// struct User {
302///     id: i32,
303///     name: String,
304/// }
305///
306/// let users = sqlx_mssql_odbc::query_as!(User, "SELECT id, name FROM users")
307///     .fetch_all(&mut conn)
308///     .await?;
309///
310/// for user in users {
311///     println!("{}: {}", user.id, user.name);
312/// }
313/// # Ok(())
314/// # }
315/// ```
316///
317/// Combine with the `derive` feature's `FromRow` derive to avoid manually
318/// writing struct definitions:
319///
320/// ```ignore
321/// # use sqlx_mssql_odbc::{MssqlConnection, FromRow};
322/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
323/// # let mut conn = MssqlConnection::connect("…").await?;
324/// #[derive(Debug, FromRow)]
325/// struct Order {
326///     id: i32,
327///     total: rust_decimal::Decimal,
328///     placed_at: chrono::NaiveDateTime,
329/// }
330///
331/// let orders = sqlx_mssql_odbc::query_as!(Order, "SELECT id, total, placed_at FROM orders")
332///     .fetch_all(&mut conn)
333///     .await?;
334/// # Ok(())
335/// # }
336/// ```
337///
338/// Requires the `macros` feature.
339#[cfg(feature = "macros")]
340#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
341#[macro_export]
342macro_rules! query_as {
343    ($out_struct:path, $query:expr) => ({
344        $crate::expand_query!(record = $out_struct, source = $query)
345    });
346    ($out_struct:path, $query:expr, $($args:tt)*) => ({
347        $crate::expand_query!(record = $out_struct, source = $query, args = [$($args)*])
348    });
349}
350
351/// Compile-time checked SQL query for MSSQL via ODBC, returning a single scalar
352/// value.
353///
354/// ```ignore
355/// # use sqlx_mssql_odbc::MssqlConnection;
356/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
357/// # let mut conn = MssqlConnection::connect("…").await?;
358/// let count: i64 = sqlx_mssql_odbc::query_scalar!("SELECT COUNT(*) FROM users")
359///     .fetch_one(&mut conn)
360///     .await?;
361///
362/// let max_id: Option<i32> = sqlx_mssql_odbc::query_scalar!("SELECT MAX(id) FROM users")
363///     .fetch_optional(&mut conn)
364///     .await?;
365/// # Ok(())
366/// # }
367/// ```
368///
369/// Requires the `macros` feature.
370#[cfg(feature = "macros")]
371#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
372#[macro_export]
373macro_rules! query_scalar {
374    ($query:expr) => (
375        $crate::expand_query!(scalar = _, source = $query)
376    );
377    ($query:expr, $($args:tt)*) => (
378        $crate::expand_query!(scalar = _, source = $query, args = [$($args)*])
379    );
380}
381
382/// Compile-time checked SQL query read from a file at compile time.
383///
384/// The file path is relative to the crate root. This keeps large queries out
385/// of your Rust source files.
386///
387/// ```ignore
388/// # use sqlx_mssql_odbc::MssqlConnection;
389/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
390/// # let mut conn = MssqlConnection::connect("…").await?;
391/// let reports = sqlx_mssql_odbc::query_file!("queries/monthly_report.sql")
392///     .fetch_all(&mut conn)
393///     .await?;
394/// # Ok(())
395/// # }
396/// ```
397///
398/// Requires the `macros` feature.
399#[cfg(feature = "macros")]
400#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
401#[macro_export]
402macro_rules! query_file {
403    ($path:literal) => ({
404        $crate::expand_query!(source_file = $path)
405    });
406    ($path:literal, $($args:tt)*) => ({
407        $crate::expand_query!(source_file = $path, args = [$($args)*])
408    });
409}
410
411/// Compile-time checked SQL query read from a file, mapping to a named struct.
412///
413/// Combines [`query_file!`] and [`query_as!`]: the SQL lives in a separate
414/// `.sql` file and rows are deserialised into the given struct.
415///
416/// ```ignore
417/// # use sqlx_mssql_odbc::MssqlConnection;
418/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
419/// # let mut conn = MssqlConnection::connect("…").await?;
420/// struct UserReport {
421///     name: String,
422///     order_count: i64,
423/// }
424///
425/// let reports = sqlx_mssql_odbc::query_file_as!(UserReport, "queries/user_reports.sql")
426///     .fetch_all(&mut conn)
427///     .await?;
428/// # Ok(())
429/// # }
430/// ```
431///
432/// Requires the `macros` feature.
433#[cfg(feature = "macros")]
434#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
435#[macro_export]
436macro_rules! query_file_as {
437    ($out_struct:path, $path:literal) => ({
438        $crate::expand_query!(record = $out_struct, source_file = $path)
439    });
440    ($out_struct:path, $path:literal, $($args:tt)*) => ({
441        $crate::expand_query!(record = $out_struct, source_file = $path, args = [$($args)*])
442    });
443}
444
445/// Compile-time checked SQL query read from a file, returning a single scalar.
446///
447/// Like [`query_file!`] but returns a single value — useful for report
448/// totals, row counts, or other aggregate queries stored in files.
449///
450/// ```ignore
451/// # use sqlx_mssql_odbc::MssqlConnection;
452/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
453/// # let mut conn = MssqlConnection::connect("…").await?;
454/// let total: f64 = sqlx_mssql_odbc::query_file_scalar!("queries/total_revenue.sql")
455///     .fetch_one(&mut conn)
456///     .await?;
457/// # Ok(())
458/// # }
459/// ```
460///
461/// Requires the `macros` feature.
462#[cfg(feature = "macros")]
463#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
464#[macro_export]
465macro_rules! query_file_scalar {
466    ($path:literal) => (
467        $crate::expand_query!(scalar = _, source_file = $path)
468    );
469    ($path:literal, $($args:tt)*) => (
470        $crate::expand_query!(scalar = _, source_file = $path, args = [$($args)*])
471    );
472}
473
474// Re-export derive macros behind `derive` feature.
475#[cfg(feature = "derive")]
476#[cfg_attr(docsrs, doc(cfg(feature = "derive")))]
477pub use sqlx_mssql_odbc_macros::{Encode, Decode, Type, FromRow};