pub struct Client<S: AsyncRead + AsyncWrite + Unpin + Send> { /* private fields */ }Expand description
Client is the main entry point to the SQL Server, providing query
execution capabilities.
A Client is created using the Config, defining the needed
connection options and capabilities.
§Example
use tokio_util::compat::TokioAsyncWriteCompatExt;
let mut config = Config::new();
config.host("0.0.0.0");
config.port(1433);
config.authentication(AuthMethod::sql_server("SA", "<Mys3cureP4ssW0rD>"));
let tcp = tokio::net::TcpStream::connect(config.get_addr()).await?;
tcp.set_nodelay(true)?;
// Client is ready to use.
let client = tds_driver::Client::connect(config, tcp.compat_write()).await?;§Cancellation safety
A single Client drives one connection and one request at a time. If a
query/execute/simple_query future — or the result stream it returns —
is dropped before the request has been sent in full and the response fully
consumed (for example under a tokio::time::timeout or a select! branch
that loses the race), the connection may be left mid-message and out of sync
with the server. A cancelled write is detected and any further use of that
connection fails cleanly; a result stream dropped mid-response cannot be
recovered. In both cases the safe course is to drop the Client and open a
new connection (a connection pool should discard the connection on error)
rather than reuse it.
§A connection that is gone
When the server drops the connection – a KILL, a severity 20+ error, a
restart, a network failure – the request in flight fails with the I/O
error. From then on every use fails with an Error::Io whose kind is
NotConnected and whose message names
that first error; the same follows a cancelled write. A pool can discard a
connection on that kind alone.
Implementations§
Source§impl<S: AsyncRead + AsyncWrite + Unpin + Send> Client<S>
impl<S: AsyncRead + AsyncWrite + Unpin + Send> Client<S>
Sourcepub async fn connect(config: Config, tcp_stream: S) -> Result<Client<S>>
pub async fn connect(config: Config, tcp_stream: S) -> Result<Client<S>>
Uses an instance of Config to specify the connection
options required to connect to the database using an established
tcp connection
Note: tcp_stream is a connected stream, so some parts of the Config
(such as multi-subnet failover, which selects between resolved
addresses) must be handled while establishing that stream, outside of
this constructor.
Sourcepub async fn execute<'a>(
&mut self,
query: impl Into<Cow<'a, str>>,
params: &[&dyn ToSql],
) -> Result<ExecuteResult>
pub async fn execute<'a>( &mut self, query: impl Into<Cow<'a, str>>, params: &[&dyn ToSql], ) -> Result<ExecuteResult>
Executes SQL statements in the SQL Server, returning the number rows
affected. Useful for INSERT, UPDATE and DELETE statements. The
query can define the parameter placement by annotating them with
@PN, where N is the index of the parameter, starting from 1. If
executing multiple queries at a time, delimit them with ; and refer to
ExecuteResult how to get results for the separate queries.
For mapping of Rust types when writing, see the documentation for
ToSql. For reading data from the database, see the documentation for
FromSql.
This API is not quite suitable for dynamic query parameters. In these
cases using a Query object might be easier.
§Example
let results = client
.execute(
"INSERT INTO ##Test (id) VALUES (@P1), (@P2), (@P3)",
&[&1i32, &2i32, &3i32],
)
.await?;Sourcepub async fn query<'a, 'b>(
&'a mut self,
query: impl Into<Cow<'b, str>>,
params: &'b [&'b dyn ToSql],
) -> Result<QueryStream<'a>>where
'a: 'b,
pub async fn query<'a, 'b>(
&'a mut self,
query: impl Into<Cow<'b, str>>,
params: &'b [&'b dyn ToSql],
) -> Result<QueryStream<'a>>where
'a: 'b,
Executes SQL statements in the SQL Server, returning resulting rows.
Useful for SELECT statements. The query can define the parameter
placement by annotating them with @PN, where N is the index of the
parameter, starting from 1. If executing multiple queries at a time,
delimit them with ; and refer to QueryStream on proper stream
handling.
For mapping of Rust types when writing, see the documentation for
ToSql. For reading data from the database, see the documentation for
FromSql.
This API can be cumbersome for dynamic query parameters. In these cases,
if fighting too much with the compiler, using a Query object might be
easier.
§Example
let stream = client
.query(
"SELECT @P1, @P2, @P3",
&[&1i32, &2i32, &3i32],
)
.await?;Sourcepub async fn simple_query<'a, 'b>(
&'a mut self,
query: impl Into<Cow<'b, str>>,
) -> Result<QueryStream<'a>>where
'a: 'b,
pub async fn simple_query<'a, 'b>(
&'a mut self,
query: impl Into<Cow<'b, str>>,
) -> Result<QueryStream<'a>>where
'a: 'b,
Execute multiple queries, delimited with ; and return multiple result
sets; one for each query.
§Example
let row = client.simple_query("SELECT 1 AS col").await?.into_row().await?.unwrap();
assert_eq!(Some(1i32), row.get("col"));§Warning
Do not use this with any user specified input. Please resort to prepared
statements using the query method.
Sourcepub async fn simple_execute<'a>(
&mut self,
query: impl Into<Cow<'a, str>>,
) -> Result<ExecuteResult>
pub async fn simple_execute<'a>( &mut self, query: impl Into<Cow<'a, str>>, ) -> Result<ExecuteResult>
Executes a batch of SQL statements sent verbatim, as simple_query
does, and returns the rows each affected rather than any result set.
The counterpart of execute for SQL with no parameters.
Because it is a plain batch and not sp_executesql, a SET option or
BEGIN TRAN in it applies to the session, as it would from a query
tool.
§Warning
Do not use this with any user specified input. Please resort to prepared
statements using the execute method.
Sourcepub async fn bulk_insert<'a>(
&'a mut self,
table: &'a str,
) -> Result<BulkLoadRequest<'a, S>>
pub async fn bulk_insert<'a>( &'a mut self, table: &'a str, ) -> Result<BulkLoadRequest<'a, S>>
Execute a BULK INSERT statement, efficiently storing a large number of
rows to a specified table. Note: make sure the input row follows the same
schema as the table, otherwise calling send() will return an error.
This is equivalent to calling bulk_insert("table_name", &["*"]) to merge
all of a tables columns.
§Example
let create_table = r#"
CREATE TABLE ##bulk_test (
id INT IDENTITY PRIMARY KEY,
val INT NOT NULL
)
"#;
client.simple_query(create_table).await?;
// Start the bulk insert with the client.
let mut req = client.bulk_insert("##bulk_test").await?;
for i in [0i32, 1i32, 2i32] {
let row = (i).into_row();
// The request will handle flushing to the wire in an optimal way,
// balancing between memory usage and IO performance.
req.send(row).await?;
}
// The request must be finalized.
let res = req.finalize().await?;
assert_eq!(3, res.total());Sourcepub async fn bulk_insert_columns<'a>(
&'a mut self,
table: &'a str,
columns: &'a [&'a str],
) -> Result<BulkLoadRequest<'a, S>>
pub async fn bulk_insert_columns<'a>( &'a mut self, table: &'a str, columns: &'a [&'a str], ) -> Result<BulkLoadRequest<'a, S>>
Execute a BULK INSERT statement, efficiently storing a large number of
rows to a specified table. Note: make sure the input row follows the same
schema as the column list, otherwise calling send() will return an error.
§Example
let create_table = r#"
CREATE TABLE ##bulk_test_columns (
id INT IDENTITY PRIMARY KEY,
foo INT NOT NULL,
bar FLOAT NOT NULL
)
"#;
client.simple_query(create_table).await?;
// Start the bulk insert with the client.
let mut req = client.bulk_insert_columns("##bulk_test_columns", &["foo", "bar"]).await?;
for (i, j) in [(0i32, 0f64), (1i32, 1f64), (2i32, 2f64)] {
let row = (i, j).into_row();
// The request will handle flushing to the wire in an optimal way,
// balancing between memory usage and IO performance.
req.send(row).await?;
}
// The request must be finalized.
let res = req.finalize().await?;
assert_eq!(3, res.total());Sourcepub async fn column_metadata(
&mut self,
table: &str,
columns: &[&str],
) -> Result<Vec<MetaDataColumn<'static>>>
pub async fn column_metadata( &mut self, table: &str, columns: &[&str], ) -> Result<Vec<MetaDataColumn<'static>>>
Retrieve the column metadata for a set of columns of a table, including the column names, types (with their size, precision and scale) and flags such as nullability and whether a column is an identity column.
Pass &["*"] as columns to return the metadata for every column of the
table.
let meta = client.column_metadata("some_table", &["*"]).await?;
assert!(meta[0].base().is_identity());Sourcepub async fn cancel_query(&mut self) -> Result<()>
pub async fn cancel_query(&mut self) -> Result<()>
Sends a TDS Attention signal to the server (packet type 0x06,
MS-TDS section 2.2.1.6) to cancel the request that is currently in
flight on this connection, and drains the acknowledging token stream so
the connection can be reused for further queries.
The server responds to the Attention signal by aborting the running
batch or RPC and returning a DONE token with the DONE_ATTN status
bit set. This method waits for that acknowledgement before returning,
discarding any remaining rows or tokens from the cancelled request.
If a CancelHandle has already cancelled the request, it only waits
for the acknowledgement, and returns at once if that has been read.
§Query cancellation and futures
Dropping a query, execute or simple_query future (for
example when a tokio::time::timeout elapses or a select! branch is
cancelled) stops the client from polling the stream, but it does not
tell the server to stop working on the request. To actually cancel the
in-flight work on the server, keep the Client and call
cancel_query on it. Because cancel_query borrows the client
mutably, it can only be issued once the borrowing result stream has
been dropped — typically from a separate task holding the client, or
after a cancelled/timed-out future has released its borrow. To cancel
without dropping it, use a CancelHandle.
Sourcepub fn cancel_handle(&self) -> CancelHandle
pub fn cancel_handle(&self) -> CancelHandle
A handle that cancels the request in flight on this connection from
outside the future awaiting it. See CancelHandle.
Sourcepub fn in_transaction(&self) -> bool
pub fn in_transaction(&self) -> bool
Whether the connection is inside a transaction, as far as the server has said.
true from the ENVCHANGE token that begins one, whether through
begin_transaction or a BEGIN TRAN in a batch, until the one that
ends it. The server also sends that one when it rolls a transaction
back on its own – a deadlock, an error under SET XACT_ABORT ON, a
ROLLBACK in a procedure – so this is how a client learns that the
transaction it thinks it is in is gone, and that its next statement
would run in autocommit.
Sourcepub async fn begin_transaction(&mut self) -> Result<()>
pub async fn begin_transaction(&mut self) -> Result<()>
Begins a new transaction using a Transaction Manager request
(TM_BEGIN_XACT, MS-TDS 2.2.6.9) instead of a BEGIN TRAN T-SQL
batch.
On success the server replies with a BeginTransaction environment
change token whose descriptor is stored in the connection context and
automatically attached to subsequent requests, scoping them to the
transaction. Commit the work with commit_transaction or discard it
with rollback_transaction.
The transaction is READ COMMITTED, SQL Server’s default, whatever
level an earlier transaction or SET TRANSACTION ISOLATION LEVEL left
on the session; the session stays READ COMMITTED afterwards. Use
begin_transaction_with_isolation for another level.
Sourcepub async fn begin_transaction_with_isolation(
&mut self,
isolation_level: IsolationLevel,
) -> Result<()>
pub async fn begin_transaction_with_isolation( &mut self, isolation_level: IsolationLevel, ) -> Result<()>
Begins a new transaction with an explicit isolation level using a
Transaction Manager request (TM_BEGIN_XACT, MS-TDS 2.2.6.9).
The level is a session setting: it stays in effect after the
transaction ends, for statements outside transactions and for later
transactions begun with IsolationLevel::Unspecified, which keeps
the current level. begin_transaction resets it to READ COMMITTED.
See begin_transaction for details on transaction scoping.
Sourcepub async fn commit_transaction(&mut self) -> Result<()>
pub async fn commit_transaction(&mut self) -> Result<()>
Commits the active transaction using a Transaction Manager request
(TM_COMMIT_XACT, MS-TDS 2.2.6.9).
After a successful commit the connection is no longer scoped to a transaction.
Sourcepub async fn rollback_transaction(&mut self) -> Result<()>
pub async fn rollback_transaction(&mut self) -> Result<()>
Rolls back the active transaction using a Transaction Manager request
(TM_ROLLBACK_XACT, MS-TDS 2.2.6.9).
After a successful rollback the connection is no longer scoped to a transaction.
Sourcepub async fn save_transaction<'a>(
&mut self,
name: impl Into<Cow<'a, str>>,
) -> Result<()>
pub async fn save_transaction<'a>( &mut self, name: impl Into<Cow<'a, str>>, ) -> Result<()>
Creates a named savepoint in the active transaction using a Transaction
Manager request (TM_SAVE_XACT, MS-TDS 2.2.6.9).
The savepoint can later be targeted by a T-SQL ROLLBACK TRANSACTION <name> to undo work performed after it while keeping the surrounding
transaction open.