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 (unchecked variant — skips database
292/// verification at compile time).
293///
294/// Like [`query!`] but does not require `DATABASE_URL` or an offline cache.
295/// Column names and types are **not** verified against the database schema;
296/// they are inferred from the query text alone.
297///
298/// Requires the `macros` feature.
299#[cfg(feature = "macros")]
300#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
301#[macro_export]
302macro_rules! query_unchecked {
303 ($query:expr) => ({
304 $crate::expand_query!(source = $query, checked = false)
305 });
306 ($query:expr, $($args:tt)*) => ({
307 $crate::expand_query!(source = $query, args = [$($args)*], checked = false)
308 });
309}
310
311/// Compile-time checked SQL query for MSSQL via ODBC, mapping to a named struct.
312///
313/// Like [`query!`] but deserialises each row into a struct you provide. The
314/// struct fields are matched to columns by name and their types are checked
315/// against the database schema at compile time.
316///
317/// ```ignore
318/// # use sqlx_mssql_odbc::MssqlConnection;
319/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
320/// # let mut conn = MssqlConnection::connect("…").await?;
321/// struct User {
322/// id: i32,
323/// name: String,
324/// }
325///
326/// let users = sqlx_mssql_odbc::query_as!(User, "SELECT id, name FROM users")
327/// .fetch_all(&mut conn)
328/// .await?;
329///
330/// for user in users {
331/// println!("{}: {}", user.id, user.name);
332/// }
333/// # Ok(())
334/// # }
335/// ```
336///
337/// Combine with the `derive` feature's `FromRow` derive to avoid manually
338/// writing struct definitions:
339///
340/// ```ignore
341/// # use sqlx_mssql_odbc::{MssqlConnection, FromRow};
342/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
343/// # let mut conn = MssqlConnection::connect("…").await?;
344/// #[derive(Debug, FromRow)]
345/// struct Order {
346/// id: i32,
347/// total: rust_decimal::Decimal,
348/// placed_at: chrono::NaiveDateTime,
349/// }
350///
351/// let orders = sqlx_mssql_odbc::query_as!(Order, "SELECT id, total, placed_at FROM orders")
352/// .fetch_all(&mut conn)
353/// .await?;
354/// # Ok(())
355/// # }
356/// ```
357///
358/// Requires the `macros` feature.
359#[cfg(feature = "macros")]
360#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
361#[macro_export]
362macro_rules! query_as {
363 ($out_struct:path, $query:expr) => ({
364 $crate::expand_query!(record = $out_struct, source = $query)
365 });
366 ($out_struct:path, $query:expr, $($args:tt)*) => ({
367 $crate::expand_query!(record = $out_struct, source = $query, args = [$($args)*])
368 });
369}
370
371/// Compile-time checked SQL query mapping to a struct (unchecked variant).
372///
373/// Like [`query_as!`] but does **not** verify column names or types against
374/// the database at compile time. Useful for CI/deploy environments that lack
375/// database connectivity.
376///
377/// Requires the `macros` feature.
378#[cfg(feature = "macros")]
379#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
380#[macro_export]
381macro_rules! query_as_unchecked {
382 ($out_struct:path, $query:expr) => ({
383 $crate::expand_query!(record = $out_struct, source = $query, checked = false)
384 });
385 ($out_struct:path, $query:expr, $($args:tt)*) => ({
386 $crate::expand_query!(record = $out_struct, source = $query, args = [$($args)*], checked = false)
387 });
388}
389
390/// Compile-time checked SQL query for MSSQL via ODBC, returning a single scalar
391/// value.
392///
393/// ```ignore
394/// # use sqlx_mssql_odbc::MssqlConnection;
395/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
396/// # let mut conn = MssqlConnection::connect("…").await?;
397/// let count: i64 = sqlx_mssql_odbc::query_scalar!("SELECT COUNT(*) FROM users")
398/// .fetch_one(&mut conn)
399/// .await?;
400///
401/// let max_id: Option<i32> = sqlx_mssql_odbc::query_scalar!("SELECT MAX(id) FROM users")
402/// .fetch_optional(&mut conn)
403/// .await?;
404/// # Ok(())
405/// # }
406/// ```
407///
408/// Requires the `macros` feature.
409#[cfg(feature = "macros")]
410#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
411#[macro_export]
412macro_rules! query_scalar {
413 ($query:expr) => (
414 $crate::expand_query!(scalar = _, source = $query)
415 );
416 ($query:expr, $($args:tt)*) => (
417 $crate::expand_query!(scalar = _, source = $query, args = [$($args)*])
418 );
419}
420
421/// Compile-time checked SQL query returning a single scalar (unchecked variant).
422///
423/// Like [`query_scalar!`] but skips database verification at compile time.
424///
425/// Requires the `macros` feature.
426#[cfg(feature = "macros")]
427#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
428#[macro_export]
429macro_rules! query_scalar_unchecked {
430 ($query:expr) => (
431 $crate::expand_query!(scalar = _, source = $query, checked = false)
432 );
433 ($query:expr, $($args:tt)*) => (
434 $crate::expand_query!(scalar = _, source = $query, args = [$($args)*], checked = false)
435 );
436}
437
438/// Compile-time checked SQL query read from a file at compile time.
439///
440/// The file path is relative to the crate root. This keeps large queries out
441/// of your Rust source files.
442///
443/// ```ignore
444/// # use sqlx_mssql_odbc::MssqlConnection;
445/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
446/// # let mut conn = MssqlConnection::connect("…").await?;
447/// let reports = sqlx_mssql_odbc::query_file!("queries/monthly_report.sql")
448/// .fetch_all(&mut conn)
449/// .await?;
450/// # Ok(())
451/// # }
452/// ```
453///
454/// Requires the `macros` feature.
455#[cfg(feature = "macros")]
456#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
457#[macro_export]
458macro_rules! query_file {
459 ($path:literal) => ({
460 $crate::expand_query!(source_file = $path)
461 });
462 ($path:literal, $($args:tt)*) => ({
463 $crate::expand_query!(source_file = $path, args = [$($args)*])
464 });
465}
466
467/// Compile-time SQL query read from a file (unchecked variant).
468///
469/// Like [`query_file!`] but does **not** verify the query against a live
470/// database or offline cache.
471///
472/// Requires the `macros` feature.
473#[cfg(feature = "macros")]
474#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
475#[macro_export]
476macro_rules! query_file_unchecked {
477 ($path:literal) => ({
478 $crate::expand_query!(source_file = $path, checked = false)
479 });
480 ($path:literal, $($args:tt)*) => ({
481 $crate::expand_query!(source_file = $path, args = [$($args)*], checked = false)
482 });
483}
484
485/// Compile-time checked SQL query read from a file, mapping to a named struct.
486///
487/// Combines [`query_file!`] and [`query_as!`]: the SQL lives in a separate
488/// `.sql` file and rows are deserialised into the given struct.
489///
490/// ```ignore
491/// # use sqlx_mssql_odbc::MssqlConnection;
492/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
493/// # let mut conn = MssqlConnection::connect("…").await?;
494/// struct UserReport {
495/// name: String,
496/// order_count: i64,
497/// }
498///
499/// let reports = sqlx_mssql_odbc::query_file_as!(UserReport, "queries/user_reports.sql")
500/// .fetch_all(&mut conn)
501/// .await?;
502/// # Ok(())
503/// # }
504/// ```
505///
506/// Requires the `macros` feature.
507#[cfg(feature = "macros")]
508#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
509#[macro_export]
510macro_rules! query_file_as {
511 ($out_struct:path, $path:literal) => ({
512 $crate::expand_query!(record = $out_struct, source_file = $path)
513 });
514 ($out_struct:path, $path:literal, $($args:tt)*) => ({
515 $crate::expand_query!(record = $out_struct, source_file = $path, args = [$($args)*])
516 });
517}
518
519/// Compile-time SQL query read from a file, mapping to a struct (unchecked
520/// variant).
521///
522/// Like [`query_file_as!`] but skips database verification at compile time.
523///
524/// Requires the `macros` feature.
525#[cfg(feature = "macros")]
526#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
527#[macro_export]
528macro_rules! query_file_as_unchecked {
529 ($out_struct:path, $path:literal) => ({
530 $crate::expand_query!(record = $out_struct, source_file = $path, checked = false)
531 });
532 ($out_struct:path, $path:literal, $($args:tt)*) => ({
533 $crate::expand_query!(record = $out_struct, source_file = $path, args = [$($args)*], checked = false)
534 });
535}
536
537/// Compile-time checked SQL query read from a file, returning a single scalar.
538///
539/// Like [`query_file!`] but returns a single value — useful for report
540/// totals, row counts, or other aggregate queries stored in files.
541///
542/// ```ignore
543/// # use sqlx_mssql_odbc::MssqlConnection;
544/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
545/// # let mut conn = MssqlConnection::connect("…").await?;
546/// let total: f64 = sqlx_mssql_odbc::query_file_scalar!("queries/total_revenue.sql")
547/// .fetch_one(&mut conn)
548/// .await?;
549/// # Ok(())
550/// # }
551/// ```
552///
553/// Requires the `macros` feature.
554#[cfg(feature = "macros")]
555#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
556#[macro_export]
557macro_rules! query_file_scalar {
558 ($path:literal) => (
559 $crate::expand_query!(scalar = _, source_file = $path)
560 );
561 ($path:literal, $($args:tt)*) => (
562 $crate::expand_query!(scalar = _, source_file = $path, args = [$($args)*])
563 );
564}
565
566/// Compile-time SQL query read from a file, returning a single scalar
567/// (unchecked variant).
568///
569/// Like [`query_file_scalar!`] but skips database verification at compile
570/// time.
571///
572/// Requires the `macros` feature.
573#[cfg(feature = "macros")]
574#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
575#[macro_export]
576macro_rules! query_file_scalar_unchecked {
577 ($path:literal) => (
578 $crate::expand_query!(scalar = _, source_file = $path, checked = false)
579 );
580 ($path:literal, $($args:tt)*) => (
581 $crate::expand_query!(scalar = _, source_file = $path, args = [$($args)*], checked = false)
582 );
583}
584
585// Re-export derive macros behind `derive` feature.
586#[cfg(feature = "derive")]
587#[cfg_attr(docsrs, doc(cfg(feature = "derive")))]
588pub use sqlx_mssql_odbc_macros::{Encode, Decode, Type, FromRow};