tiberius/lib.rs
1//! A focused raw bulk-load fork of Tiberius, an asynchronous,
2//! runtime-independent, pure-rust Tabular Data Stream (TDS) implementation for
3//! Microsoft SQL Server.
4//!
5//! This package is published as `tiberius-raw-bulk`, but the Rust library crate
6//! name remains `tiberius` for compatibility with upstream Tiberius users:
7//!
8//! ```toml
9//! tiberius = { package = "tiberius-raw-bulk", version = "0.12.3-raw-bulk.12" }
10//! ```
11//!
12//! The fork keeps upstream query, authentication, TLS, and connection behavior
13//! intact outside its raw bulk-load extension points. Application-specific
14//! planning, Arrow mapping, and row encoding logic belong in downstream crates.
15//!
16//! # Raw bulk-load extensions
17//!
18//! The fork adds extension points for callers that want to plan or encode bulk
19//! rows outside Tiberius while still using Tiberius for connection handling and
20//! TDS packet framing.
21//!
22//! Metadata discovery and request setup are available through:
23//!
24//! - [`Client::bulk_insert_columns`]
25//! - [`Client::bulk_insert_with_columns`]
26//! - [`BulkLoadRequest::columns`]
27//!
28//! Raw row append APIs are available through:
29//!
30//! - [`BulkLoadRequest::send_raw_row_payload`]
31//! - [`BulkLoadRequest::send_raw_rows_payload`]
32//! - [`BulkLoadRequest::send_raw_rows_payload_checked`]
33//! - [`BulkLoadRequest::send_raw_rows_with`]
34//! - [`RawRowsAppendBuffer`]
35//!
36//! Direct packet writes can be enabled with
37//! [`BulkLoadRequest::enable_direct_packet_writes`] and inspected with
38//! [`BulkLoadRequest::direct_packet_writes_enabled`].
39//!
40//! Profiling and statistics APIs are opt-in through the `bulk-load-profile`
41//! feature. When enabled, the crate exposes packet counters and write timing
42//! breakdowns for bulk-load requests.
43//!
44//! # Connecting with async-std
45//!
46//! Being not bound to any single runtime, a `TcpStream` must be created
47//! separately and injected to the [`Client`].
48//!
49//! ```no_run
50//! use tiberius::{Client, Config, Query, AuthMethod};
51//! use async_std::net::TcpStream;
52//!
53//! #[async_std::main]
54//! async fn main() -> anyhow::Result<()> {
55//! // Using the builder method to construct the options.
56//! let mut config = Config::new();
57//!
58//! config.host("localhost");
59//! config.port(1433);
60//!
61//! // Using SQL Server authentication.
62//! config.authentication(AuthMethod::sql_server("SA", "<YourStrong@Passw0rd>"));
63//!
64//! // on production, it is not a good idea to do this
65//! config.trust_cert();
66//!
67//! // Taking the address from the configuration, using async-std's
68//! // TcpStream to connect to the server.
69//! let tcp = TcpStream::connect(config.get_addr()).await?;
70//!
71//! // We'll disable the Nagle algorithm. Buffering is handled
72//! // internally with a `Sink`.
73//! tcp.set_nodelay(true)?;
74//!
75//! // Handling TLS, login and other details related to the SQL Server.
76//! let mut client = Client::connect(config, tcp).await?;
77//!
78//! // Constructing a query object with one parameter annotated with `@P1`.
79//! // This requires us to bind a parameter that will then be used in
80//! // the statement.
81//! let mut select = Query::new("SELECT @P1");
82//! select.bind(-4i32);
83//!
84//! // A response to a query is a stream of data, that must be
85//! // polled to the end before querying again. Using streams allows
86//! // fetching data in an asynchronous manner, if needed.
87//! let stream = select.query(&mut client).await?;
88//!
89//! // In this case, we know we have only one query, returning one row
90//! // and one column, so calling `into_row` will consume the stream
91//! // and return us the first row of the first result.
92//! let row = stream.into_row().await?;
93//!
94//! assert_eq!(Some(-4i32), row.unwrap().get(0));
95//!
96//! Ok(())
97//! }
98//! ```
99//!
100//! # Connecting with Tokio
101//!
102//! Tokio is using their own version of `AsyncRead` and `AsyncWrite` traits,
103//! meaning that when wanting to use Tiberius with Tokio, their `TcpStream`
104//! needs to be wrapped in Tokio's `Compat` module.
105//!
106//! ```no_run
107//! use tiberius::{Client, Config, AuthMethod};
108//! use tokio::net::TcpStream;
109//! use tokio_util::compat::TokioAsyncWriteCompatExt;
110//!
111//! #[tokio::main]
112//! async fn main() -> anyhow::Result<()> {
113//! let mut config = Config::new();
114//!
115//! config.host("localhost");
116//! config.port(1433);
117//! config.authentication(AuthMethod::sql_server("SA", "<YourStrong@Passw0rd>"));
118//! config.trust_cert(); // on production, it is not a good idea to do this
119//!
120//! let tcp = TcpStream::connect(config.get_addr()).await?;
121//! tcp.set_nodelay(true)?;
122//!
123//! // To be able to use Tokio's tcp, we're using the `compat_write` from
124//! // the `TokioAsyncWriteCompatExt` to get a stream compatible with the
125//! // traits from the `futures` crate.
126//! let mut client = Client::connect(config, tcp.compat_write()).await?;
127//! # client.query("SELECT @P1", &[&-4i32]).await?;
128//!
129//! Ok(())
130//! }
131//! ```
132//!
133//! # Ways of querying
134//!
135//! Tiberius offers two ways to query the database: directly from the [`Client`]
136//! with the [`Client#query`] and [`Client#execute`], or additionally through
137//! the [`Query`] object.
138//!
139//! ### With the client methods
140//!
141//! When the query parameters are known when writing the code, the client methods
142//! are easy to use.
143//!
144//! ```no_run
145//! # use tiberius::{Client, Config, AuthMethod};
146//! # use tokio::net::TcpStream;
147//! # use tokio_util::compat::TokioAsyncWriteCompatExt;
148//! # #[tokio::main]
149//! # async fn main() -> anyhow::Result<()> {
150//! # let mut config = Config::new();
151//! # config.host("localhost");
152//! # config.port(1433);
153//! # config.authentication(AuthMethod::sql_server("SA", "<YourStrong@Passw0rd>"));
154//! # config.trust_cert();
155//! # let tcp = TcpStream::connect(config.get_addr()).await?;
156//! # tcp.set_nodelay(true)?;
157//! # let mut client = Client::connect(config, tcp.compat_write()).await?;
158//! let _res = client.query("SELECT @P1", &[&-4i32]).await?;
159//! # Ok(())
160//! # }
161//! ```
162//!
163//! ### With the Query object
164//!
165//! In case of needing to pass the parameters from a dynamic collection, or if
166//! wanting to pass them by-value, use the [`Query`] object.
167//!
168//! ```no_run
169//! # use tiberius::{Client, Query, Config, AuthMethod};
170//! # use tokio::net::TcpStream;
171//! # use tokio_util::compat::TokioAsyncWriteCompatExt;
172//! # #[tokio::main]
173//! # async fn main() -> anyhow::Result<()> {
174//! # let mut config = Config::new();
175//! # config.host("localhost");
176//! # config.port(1433);
177//! # config.authentication(AuthMethod::sql_server("SA", "<YourStrong@Passw0rd>"));
178//! # config.trust_cert();
179//! # let tcp = TcpStream::connect(config.get_addr()).await?;
180//! # tcp.set_nodelay(true)?;
181//! # let mut client = Client::connect(config, tcp.compat_write()).await?;
182//! let params = vec![String::from("foo"), String::from("bar")];
183//! let mut select = Query::new("SELECT @P1, @P2, @P3");
184//!
185//! for param in params.into_iter() {
186//! select.bind(param);
187//! }
188//!
189//! let _res = select.query(&mut client).await?;
190//! # Ok(())
191//! # }
192//! ```
193//!
194//! # Authentication
195//!
196//! Tiberius supports different [ways of authentication] to the SQL Server:
197//!
198//! - SQL Server authentication uses the facilities of the database to
199//! authenticate the user.
200//! - On Windows, you can authenticate using the currently logged in user or
201//! specified Windows credentials.
202//! - If enabling the `integrated-auth-gssapi` feature, it is possible to login
203//! with the currently active Kerberos credentials.
204//!
205//! ## AAD(Azure Active Directory) Authentication
206//!
207//! Tiberius supports AAD authentication by taking an AAD token. Suggest using
208//! [azure_identity](https://crates.io/crates/azure_identity) crate to retrieve
209//! the token, and config tiberius with token. There is an example in examples
210//! folder on how to setup this.
211//!
212//! # TLS
213//!
214//! When compiled using the default features, a TLS encryption will be available
215//! and by default, used for all traffic. TLS is handled with the given
216//! `TcpStream`. Please see the documentation for [`EncryptionLevel`] for
217//! details.
218//!
219//! # SQL Browser
220//!
221//! On Windows platforms, connecting to the SQL Server might require going through
222//! the SQL Browser service to get the correct port for the named instance. This
223//! feature requires one of the `sql-browser-smol`, `sql-browser-tokio`, or
224//! `sql-browser-async-std` feature flags to be enabled and has a bit different
225//! way of connecting.
226//!
227//! `sql-browser-async-std` is deprecated because `async-std` is discontinued
228//! upstream. It remains available for existing users. New code should prefer
229//! `sql-browser-smol` with `async_net::TcpStream` as the closest migration path,
230//! or `sql-browser-tokio` when the application already uses Tokio.
231//!
232//! The following compatibility example uses async-std:
233//!
234//! ```no_run
235//! # #[cfg(feature = "sql-browser-async-std")]
236//! use tiberius::{Client, Config, AuthMethod};
237//! # #[cfg(feature = "sql-browser-async-std")]
238//! use async_std::net::TcpStream;
239//!
240//! // An extra trait that allows connecting to a named instance with the given
241//! // `TcpStream`.
242//! # #[cfg(feature = "sql-browser-async-std")]
243//! use tiberius::SqlBrowser;
244//!
245//! #[async_std::main]
246//! # #[cfg(feature = "sql-browser-async-std")]
247//! async fn main() -> anyhow::Result<()> {
248//! let mut config = Config::new();
249//!
250//! config.authentication(AuthMethod::sql_server("SA", "<password>"));
251//! config.host("localhost");
252//!
253//! // The default port of SQL Browser
254//! config.port(1434);
255//!
256//! // The name of the database server instance.
257//! config.instance_name("INSTANCE");
258//!
259//! // on production, it is not a good idea to do this
260//! config.trust_cert();
261//!
262//! // This will create a new `TcpStream` from `async-std`, connected to the
263//! // right port of the named instance.
264//! let tcp = TcpStream::connect_named(&config).await?;
265//!
266//! // And from here on continue the connection process in a normal way.
267//! let mut client = Client::connect(config, tcp).await?;
268//! # client.query("SELECT @P1", &[&-4i32]).await?;
269//! Ok(())
270//! }
271//! # #[cfg(not(feature = "sql-browser-async-std"))]
272//! # fn main() {}
273//! ```
274//!
275//! # Other features
276//!
277//! - If using an [ADO.NET connection string], it is possible to create a
278//! [`Config`] from one. Please see the documentation for
279//! [`from_ado_string`] for details.
280//! - If wanting to use Tiberius with SQL Server version 2005, one must
281//! disable the `tds73` feature.
282//!
283//! [`EncryptionLevel`]: enum.EncryptionLevel.html
284//! [`Client`]: struct.Client.html
285//! [`Client#query`]: struct.Client.html#method.query
286//! [`Client#execute`]: struct.Client.html#method.execute
287//! [`Query`]: struct.Query.html
288//! [`Query#bind`]: struct.Query.html#method.bind
289//! [`Config`]: struct.Config.html
290//! [`from_ado_string`]: struct.Config.html#method.from_ado_string
291//! [`time`]: time/index.html
292//! [ways of authentication]: enum.AuthMethod.html
293//! [ADO.NET connection string]: https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/connection-strings
294#![cfg_attr(feature = "docs", feature(doc_cfg))]
295#![recursion_limit = "512"]
296#![warn(missing_docs)]
297#![warn(missing_debug_implementations, rust_2018_idioms)]
298#![doc(test(attr(deny(rust_2018_idioms, warnings))))]
299#![doc(test(attr(allow(unused_extern_crates, unused_variables))))]
300
301#[cfg(feature = "bigdecimal")]
302pub(crate) extern crate bigdecimal_ as bigdecimal;
303
304#[macro_use]
305mod macros;
306
307mod client;
308mod from_sql;
309mod query;
310mod sql_read_bytes;
311mod to_sql;
312
313pub mod error;
314mod result;
315mod row;
316mod tds;
317
318mod sql_browser;
319
320pub use client::{AuthMethod, Client, Config};
321pub(crate) use error::Error;
322pub use from_sql::{FromSql, FromSqlOwned};
323pub use query::Query;
324pub use result::*;
325pub use row::{Column, ColumnType, Row};
326pub use sql_browser::SqlBrowser;
327#[cfg(feature = "bulk-load-profile")]
328pub use tds::codec::{
329 BulkLoadConnectionWriteStats, BulkLoadDirectPacketWriteStats, BulkLoadPacketStats,
330 BulkLoadStats, BulkLoadWriteTimingStats,
331};
332pub use tds::{
333 codec::{
334 BulkLoadColumn, BulkLoadColumns, BulkLoadRequest, ColumnData, ColumnFlag, FixedLenType,
335 IntoRow, RawRowsAppend, RawRowsAppendBuffer, TokenRow, TypeInfo, TypeLength, VarLenContext,
336 VarLenType,
337 },
338 numeric,
339 stream::QueryStream,
340 time, xml, Collation, EncryptionLevel,
341};
342pub use to_sql::{IntoSql, ToSql};
343pub use uuid::Uuid;
344
345use sql_read_bytes::*;
346use tds::codec::*;
347
348/// An alias for a result that holds crate's error type as the error.
349pub type Result<T> = std::result::Result<T, Error>;
350
351pub(crate) fn get_driver_version() -> u64 {
352 env!("CARGO_PKG_VERSION")
353 .splitn(6, '.')
354 .enumerate()
355 .fold(0u64, |acc, part| match part.1.parse::<u64>() {
356 Ok(num) => acc | num << (part.0 * 8),
357 _ => acc | 0 << (part.0 * 8),
358 })
359}