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