Skip to main content

mssql_client/
lib.rs

1//! # mssql-client
2//!
3//! High-level async SQL Server client with type-state connection management.
4//!
5//! This is the primary public API surface for the rust-mssql-driver project.
6//! It provides a type-safe, ergonomic interface for working with SQL Server
7//! databases.
8//!
9//! ## Features
10//!
11//! - **Type-state pattern**: Compile-time enforcement of connection states
12//! - **Async/await**: Built on Tokio for efficient async I/O
13//! - **Prepared statements**: Automatic caching with LRU eviction
14//! - **Transactions**: Full transaction support with savepoints
15//! - **Azure support**: Automatic routing and failover handling
16//! - **Streaming results**: Memory-efficient processing of large result sets
17//!
18//! ## Type-State Connection Management
19//!
20//! The client uses a compile-time type-state pattern that ensures invalid
21//! operations are caught at compile time rather than runtime:
22//!
23//! ```text
24//! Disconnected -> Ready (via connect())
25//! Ready -> InTransaction (via begin_transaction())
26//! Ready -> Streaming (via query that returns a stream)
27//! InTransaction -> Ready (via commit() or rollback())
28//! ```
29//!
30//! ## Example
31//!
32//! ```rust,ignore
33//! use mssql_client::{Client, Config};
34//!
35//! #[tokio::main]
36//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
37//!     let config = Config::from_connection_string(
38//!         "Server=localhost;Database=test;User Id=sa;Password=Password123;"
39//!     )?;
40//!
41//!     let mut client = Client::connect(config).await?;
42//!
43//!     // Execute a query with parameters
44//!     let rows = client
45//!         .query("SELECT * FROM users WHERE id = @p1", &[&1])
46//!         .await?;
47//!
48//!     for row in rows {
49//!         let name: String = row.get(0)?;
50//!         println!("User: {}", name);
51//!     }
52//!
53//!     // Transactions with savepoint support
54//!     let mut tx = client.begin_transaction().await?;
55//!     tx.execute("INSERT INTO users (name) VALUES (@p1)", &[&"Alice"]).await?;
56//!
57//!     // Create a savepoint for partial rollback
58//!     let sp = tx.savepoint("before_update").await?;
59//!     tx.execute("UPDATE users SET active = 1", &[]).await?;
60//!
61//!     // Rollback to savepoint if needed
62//!     // tx.rollback_to(&sp).await?;
63//!
64//!     tx.commit().await?;
65//!
66//!     Ok(())
67//! }
68//! ```
69
70#![warn(missing_docs)]
71#![deny(unsafe_code)]
72
73// Module dependency graph (acyclic):
74//
75//   client ──→ config, state, error, stream, transaction, statement_cache
76//     ├── connect.rs ──→ config, state, instrumentation, mssql_tls, mssql_codec, tds_protocol
77//     ├── params.rs  ──→ mssql_types, tds_protocol
78//     └── response.rs ──→ error, mssql_codec, tds_protocol
79//   procedure ──→ client, error, state, stream, tds_protocol
80//   stream ──→ error, row
81//   row ──→ blob, error, mssql_types
82//   config ──→ mssql_auth, mssql_tls, tds_protocol
83//   bulk ──→ error, mssql_types, tds_protocol
84//   cancel ──→ error, mssql_codec, mssql_tls
85//   encryption ──→ mssql_auth, tds_protocol
86//   column_parser ──→ error, mssql_types, tds_protocol
87
88pub mod blob;
89pub(crate) mod browser;
90pub mod bulk;
91pub mod cancel;
92pub mod change_tracking;
93pub mod client;
94#[cfg(feature = "always-encrypted")]
95pub(crate) mod column_decryptor;
96pub(crate) mod column_parser;
97pub mod config;
98pub mod encryption;
99pub mod error;
100#[cfg(all(windows, feature = "filestream"))]
101#[allow(unsafe_code)] // Win32 FFI for OpenSqlFilestream; see SAFETY comments in each unsafe block
102pub mod filestream;
103pub mod from_row;
104pub mod instrumentation;
105pub mod procedure;
106pub mod query;
107pub mod row;
108pub mod state;
109pub mod statement_cache;
110pub mod stream;
111pub mod to_params;
112pub mod transaction;
113pub mod tvp;
114pub(crate) mod validation;
115
116// Re-export commonly used types
117pub use bulk::{BulkColumn, BulkInsert, BulkInsertBuilder, BulkInsertResult, BulkOptions};
118pub use cancel::CancelHandle;
119pub use client::Client;
120pub use config::{ApplicationIntent, Config, RedirectConfig, RetryPolicy, TimeoutConfig};
121pub use error::{Error, SharedIoError};
122
123// Re-export TDS version for configuration
124pub use from_row::{FromRow, MapRows, RowIteratorExt};
125pub use mssql_auth::Credentials;
126pub use tds_protocol::version::TdsVersion;
127
128// Secure credential types (with zeroize feature)
129#[cfg(feature = "zeroize")]
130pub use mssql_auth::{SecretString, SecureCredentials};
131pub use mssql_types::{FromSql, SqlValue, ToSql};
132pub use procedure::ProcedureBuilder;
133pub use query::Query;
134pub use row::{Column, Row};
135pub use state::{
136    Connected, ConnectionState, Disconnected, InTransaction, ProtocolState, Ready, Streaming,
137};
138pub use statement_cache::{PreparedStatement, StatementCache, StatementCacheConfig};
139pub use stream::{
140    ExecuteResult, MultiResultStream, OutputParam, ProcedureResult, QueryStream, ResultSet,
141};
142pub use to_params::{NamedParam, ParamList, ToParams};
143pub use transaction::{IsolationLevel, SavePoint, Transaction};
144pub use tvp::{Tvp, TvpColumn, TvpRow, TvpValue};
145
146// FILESTREAM support (Windows only)
147#[cfg(all(windows, feature = "filestream"))]
148pub use filestream::{FileStream, FileStreamAccess, open_options as filestream_options};
149
150// Always Encrypted types
151#[cfg(feature = "always-encrypted")]
152pub use encryption::EncryptionContext;
153pub use encryption::{
154    EncryptionConfig, ParameterCryptoInfo, ParameterEncryptionInfo, ResultSetEncryptionInfo,
155};
156
157// OpenTelemetry instrumentation (available whether or not otel feature is enabled)
158pub use instrumentation::{
159    DatabaseMetrics, OperationTimer, SanitizationConfig, attributes, metric_names, span_names,
160};
161
162// Change Tracking support
163pub use change_tracking::{
164    ChangeMetadata, ChangeOperation, ChangeTracking, ChangeTrackingQuery, SyncVersionStatus,
165};
166
167#[cfg(test)]
168mod auto_trait_tests {
169    //! Compile-time assertions that key async types are Send + Sync.
170    //!
171    //! These tests catch regressions where a type accidentally becomes
172    //! !Send or !Sync due to interior changes (e.g., adding an Rc, Cell,
173    //! or non-Send future). They cost nothing at runtime.
174
175    use super::*;
176
177    fn assert_send<T: Send>() {}
178    fn assert_sync<T: Sync>() {}
179
180    // --- Type-state Client variants ---
181    #[test]
182    fn client_ready_is_send_sync() {
183        assert_send::<Client<Ready>>();
184        assert_sync::<Client<Ready>>();
185    }
186
187    #[test]
188    fn client_in_transaction_is_send_sync() {
189        assert_send::<Client<InTransaction>>();
190        assert_sync::<Client<InTransaction>>();
191    }
192
193    #[test]
194    fn client_disconnected_is_send_sync() {
195        assert_send::<Client<Disconnected>>();
196        assert_sync::<Client<Disconnected>>();
197    }
198
199    #[test]
200    fn client_connected_is_send_sync() {
201        assert_send::<Client<Connected>>();
202        assert_sync::<Client<Connected>>();
203    }
204
205    #[test]
206    fn client_streaming_is_send_sync() {
207        assert_send::<Client<Streaming>>();
208        assert_sync::<Client<Streaming>>();
209    }
210
211    // --- Configuration ---
212    #[test]
213    fn config_is_send_sync() {
214        assert_send::<Config>();
215        assert_sync::<Config>();
216    }
217
218    // --- Streaming types ---
219    #[test]
220    fn query_stream_is_send_sync() {
221        assert_send::<QueryStream<'_>>();
222        assert_sync::<QueryStream<'_>>();
223    }
224
225    #[test]
226    fn multi_result_stream_is_send_sync() {
227        assert_send::<MultiResultStream<'_>>();
228        assert_sync::<MultiResultStream<'_>>();
229    }
230
231    #[test]
232    fn result_set_is_send_sync() {
233        assert_send::<ResultSet>();
234        assert_sync::<ResultSet>();
235    }
236
237    #[test]
238    fn execute_result_is_send_sync() {
239        assert_send::<ExecuteResult>();
240        assert_sync::<ExecuteResult>();
241    }
242
243    #[test]
244    fn procedure_result_is_send_sync() {
245        assert_send::<ProcedureResult>();
246        assert_sync::<ProcedureResult>();
247    }
248
249    #[test]
250    fn procedure_builder_is_send_sync() {
251        assert_send::<ProcedureBuilder<'_, Ready>>();
252        assert_sync::<ProcedureBuilder<'_, Ready>>();
253    }
254
255    // --- Bulk insert types ---
256    #[test]
257    fn bulk_insert_is_send_sync() {
258        assert_send::<BulkInsert>();
259        assert_sync::<BulkInsert>();
260    }
261
262    #[test]
263    fn bulk_insert_builder_is_send_sync() {
264        assert_send::<BulkInsertBuilder>();
265        assert_sync::<BulkInsertBuilder>();
266    }
267
268    #[test]
269    fn bulk_options_is_send_sync() {
270        assert_send::<BulkOptions>();
271        assert_sync::<BulkOptions>();
272    }
273
274    // --- Cancel handle ---
275    #[test]
276    fn cancel_handle_is_send_sync() {
277        assert_send::<CancelHandle>();
278        assert_sync::<CancelHandle>();
279    }
280
281    // --- Row and column types ---
282    #[test]
283    fn row_is_send_sync() {
284        assert_send::<Row>();
285        assert_sync::<Row>();
286    }
287
288    #[test]
289    fn column_is_send_sync() {
290        assert_send::<Column>();
291        assert_sync::<Column>();
292    }
293
294    // --- Statement cache ---
295    #[test]
296    fn statement_cache_is_send_sync() {
297        assert_send::<StatementCache>();
298        assert_sync::<StatementCache>();
299    }
300
301    // --- Error type ---
302    #[test]
303    fn error_is_send_sync() {
304        assert_send::<Error>();
305        assert_sync::<Error>();
306    }
307}