use tokio::runtime::{Builder, Runtime};
use crate::command::Command;
use crate::config::Config;
use crate::dataset::DataSet;
use crate::error::{Error, Result};
use crate::row::{Column, Row};
use crate::transaction::IsolationLevel;
use crate::value::{FromSql, ToSql};
fn build_runtime() -> Result<Runtime> {
Builder::new_multi_thread()
.worker_threads(1)
.enable_all()
.thread_name("tdsql-connection")
.build()
.map_err(Error::Io)
}
struct OwnedRuntime(Option<Runtime>);
impl std::ops::Deref for OwnedRuntime {
type Target = Runtime;
fn deref(&self) -> &Runtime {
self.0.as_ref().expect("taken only in Drop")
}
}
impl Drop for OwnedRuntime {
fn drop(&mut self) {
if let Some(runtime) = self.0.take()
&& tokio::runtime::Handle::try_current().is_ok()
{
runtime.shutdown_background();
}
}
}
pub struct Client {
runtime: OwnedRuntime,
inner: crate::Client,
}
impl std::fmt::Debug for Client {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("blocking::Client")
.field("inner", &self.inner)
.finish()
}
}
impl Client {
pub fn connect(config: &Config) -> Result<Self> {
if tokio::runtime::Handle::try_current().is_ok() {
return Err(Error::BlockingInAsync);
}
let runtime = build_runtime()?;
let inner = runtime.block_on(crate::Client::connect(config))?;
Ok(Self {
runtime: OwnedRuntime(Some(runtime)),
inner,
})
}
pub fn connect_str(connection_string: &str) -> Result<Self> {
Self::connect(&Config::from_ado_string(connection_string))
}
pub fn query(&mut self, sql: &str, params: &[&dyn ToSql]) -> Result<Vec<Row>> {
self.runtime.block_on(self.inner.query(sql, params))
}
pub fn query_stream(&mut self, sql: &str, params: &[&dyn ToSql]) -> Result<RowIter<'_>> {
let Self { runtime, inner } = self;
let rows = runtime.block_on(inner.query_stream(sql, params))?;
Ok(RowIter {
runtime,
inner: rows,
})
}
pub fn query_one(&mut self, sql: &str, params: &[&dyn ToSql]) -> Result<Row> {
self.runtime.block_on(self.inner.query_one(sql, params))
}
pub fn query_opt(&mut self, sql: &str, params: &[&dyn ToSql]) -> Result<Option<Row>> {
self.runtime.block_on(self.inner.query_opt(sql, params))
}
pub fn query_scalar<T: FromSql>(&mut self, sql: &str, params: &[&dyn ToSql]) -> Result<T> {
self.runtime.block_on(self.inner.query_scalar(sql, params))
}
pub fn execute(&mut self, sql: &str, params: &[&dyn ToSql]) -> Result<u64> {
self.runtime.block_on(self.inner.execute(sql, params))
}
pub fn batch(&mut self, sql: &str) -> Result<Vec<Row>> {
self.runtime.block_on(self.inner.batch(sql))
}
pub fn batch_dataset(&mut self, sql: &str) -> Result<DataSet> {
self.runtime.block_on(self.inner.batch_dataset(sql))
}
pub fn query_dataset(&mut self, command: &Command) -> Result<DataSet> {
self.runtime.block_on(self.inner.query_dataset(command))
}
pub fn execute_command(&mut self, command: &Command) -> Result<u64> {
self.runtime.block_on(self.inner.execute_command(command))
}
pub fn transaction(&mut self) -> Result<Transaction<'_>> {
self.transaction_with_isolation(IsolationLevel::ReadCommitted)
}
pub fn transaction_with_isolation(&mut self, level: IsolationLevel) -> Result<Transaction<'_>> {
let Self { runtime, inner } = self;
let tx = runtime.block_on(inner.transaction_with_isolation(level))?;
Ok(Transaction { runtime, inner: tx })
}
pub fn close(self) -> Result<()> {
let Self { runtime, inner } = self;
runtime.block_on(inner.close())
}
}
pub struct Transaction<'a> {
runtime: &'a Runtime,
inner: crate::Transaction<'a>,
}
impl std::fmt::Debug for Transaction<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("blocking::Transaction")
.field("inner", &self.inner)
.finish()
}
}
impl Transaction<'_> {
pub fn commit(self) -> Result<()> {
let Self { runtime, inner } = self;
runtime.block_on(inner.commit())
}
pub fn rollback(self) -> Result<()> {
let Self { runtime, inner } = self;
runtime.block_on(inner.rollback())
}
pub fn transaction(&mut self) -> Result<Transaction<'_>> {
let Self { runtime, inner } = self;
let tx = runtime.block_on(inner.transaction())?;
Ok(Transaction { runtime, inner: tx })
}
pub fn savepoint(&mut self, name: impl Into<String>) -> Result<Transaction<'_>> {
let Self { runtime, inner } = self;
let tx = runtime.block_on(inner.savepoint(name))?;
Ok(Transaction { runtime, inner: tx })
}
pub fn query(&mut self, sql: &str, params: &[&dyn ToSql]) -> Result<Vec<Row>> {
self.runtime.block_on(self.inner.query(sql, params))
}
pub fn query_stream(&mut self, sql: &str, params: &[&dyn ToSql]) -> Result<RowIter<'_>> {
let Self { runtime, inner } = self;
let rows = runtime.block_on(inner.query_stream(sql, params))?;
Ok(RowIter {
runtime,
inner: rows,
})
}
pub fn query_one(&mut self, sql: &str, params: &[&dyn ToSql]) -> Result<Row> {
self.runtime.block_on(self.inner.query_one(sql, params))
}
pub fn query_opt(&mut self, sql: &str, params: &[&dyn ToSql]) -> Result<Option<Row>> {
self.runtime.block_on(self.inner.query_opt(sql, params))
}
pub fn query_scalar<T: FromSql>(&mut self, sql: &str, params: &[&dyn ToSql]) -> Result<T> {
self.runtime.block_on(self.inner.query_scalar(sql, params))
}
pub fn execute(&mut self, sql: &str, params: &[&dyn ToSql]) -> Result<u64> {
self.runtime.block_on(self.inner.execute(sql, params))
}
pub fn batch(&mut self, sql: &str) -> Result<Vec<Row>> {
self.runtime.block_on(self.inner.batch(sql))
}
pub fn batch_dataset(&mut self, sql: &str) -> Result<DataSet> {
self.runtime.block_on(self.inner.batch_dataset(sql))
}
pub fn query_dataset(&mut self, command: &Command) -> Result<DataSet> {
self.runtime.block_on(self.inner.query_dataset(command))
}
pub fn execute_command(&mut self, command: &Command) -> Result<u64> {
self.runtime.block_on(self.inner.execute_command(command))
}
}
pub struct RowIter<'a> {
runtime: &'a Runtime,
inner: crate::RowStream<'a>,
}
impl std::fmt::Debug for RowIter<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("blocking::RowIter")
.field("inner", &self.inner)
.finish()
}
}
impl RowIter<'_> {
pub fn columns(&self) -> &[Column] {
self.inner.columns()
}
}
impl Iterator for RowIter<'_> {
type Item = Result<Row>;
fn next(&mut self) -> Option<Self::Item> {
self.runtime.block_on(self.inner.try_next()).transpose()
}
}
mod sealed {
use tokio::runtime::Runtime;
pub trait Sealed {
fn parts(&mut self) -> (&Runtime, &mut crate::Client);
}
}
pub(crate) use sealed::Sealed;
impl Sealed for Client {
fn parts(&mut self) -> (&Runtime, &mut crate::Client) {
let Self { runtime, inner } = self;
(runtime, inner)
}
}
impl Sealed for Transaction<'_> {
fn parts(&mut self) -> (&Runtime, &mut crate::Client) {
let Self { runtime, inner } = self;
(runtime, inner.client_mut())
}
}
impl<T: Sealed + ?Sized> Sealed for &mut T {
fn parts(&mut self) -> (&Runtime, &mut crate::Client) {
(**self).parts()
}
}
pub trait Executor: Sealed {
fn transaction(&mut self) -> Result<Transaction<'_>>;
fn query(&mut self, sql: &str, params: &[&dyn ToSql]) -> Result<Vec<Row>> {
let (runtime, client) = self.parts();
runtime.block_on(client.query(sql, params))
}
fn query_stream(&mut self, sql: &str, params: &[&dyn ToSql]) -> Result<RowIter<'_>> {
let (runtime, client) = self.parts();
let inner = runtime.block_on(client.query_stream(sql, params))?;
Ok(RowIter { runtime, inner })
}
fn query_one(&mut self, sql: &str, params: &[&dyn ToSql]) -> Result<Row> {
let (runtime, client) = self.parts();
runtime.block_on(client.query_one(sql, params))
}
fn query_opt(&mut self, sql: &str, params: &[&dyn ToSql]) -> Result<Option<Row>> {
let (runtime, client) = self.parts();
runtime.block_on(client.query_opt(sql, params))
}
fn query_scalar<T: FromSql>(&mut self, sql: &str, params: &[&dyn ToSql]) -> Result<T> {
let (runtime, client) = self.parts();
runtime.block_on(client.query_scalar(sql, params))
}
fn execute(&mut self, sql: &str, params: &[&dyn ToSql]) -> Result<u64> {
let (runtime, client) = self.parts();
runtime.block_on(client.execute(sql, params))
}
fn batch(&mut self, sql: &str) -> Result<Vec<Row>> {
let (runtime, client) = self.parts();
runtime.block_on(client.batch(sql))
}
fn batch_dataset(&mut self, sql: &str) -> Result<DataSet> {
let (runtime, client) = self.parts();
runtime.block_on(client.batch_dataset(sql))
}
fn query_dataset(&mut self, command: &Command) -> Result<DataSet> {
let (runtime, client) = self.parts();
runtime.block_on(client.query_dataset(command))
}
fn execute_command(&mut self, command: &Command) -> Result<u64> {
let (runtime, client) = self.parts();
runtime.block_on(client.execute_command(command))
}
}
impl Executor for Client {
fn transaction(&mut self) -> Result<Transaction<'_>> {
Client::transaction(self)
}
}
impl Executor for Transaction<'_> {
fn transaction(&mut self) -> Result<Transaction<'_>> {
Transaction::transaction(self)
}
}
impl<T: Executor + ?Sized> Executor for &mut T {
fn transaction(&mut self) -> Result<Transaction<'_>> {
(**self).transaction()
}
}