< 返回我的博客

hbgjh 发表于 2021-02-14 22:35

一直用c#去年开始rust边学边练,一直期待和MSSQL连接案例。

评论区

写评论
c5soft 2021-02-19 13:14

连接SQL Server目前比较成熟的方案是使用Tiberius,async/await, 用bb8做pool, bb8-tiberius的作者好像没再维护这个项目了,我pr了一次也没理我。crates.io上的bb8-tiberius目前还停留在tiberius0.4, 需要自己改一下, 下面是配合tiberius 0.5.8使用的bb8-tiberius,可以在自己的项目中作为一个mod直接使用:

#![allow(dead_code)]
/// The error container
#[derive(Debug, thiserror::Error)]
pub enum Error {
    #[error(transparent)]
    Tiberius(#[from] tiberius::error::Error),
    #[error(transparent)]
    Io(#[from] std::io::Error),
}

/// Implemented for `&str` (ADO-style string) and `tiberius::Config`
pub trait IntoConfig {
    fn into_config(self) -> tiberius::Result<tiberius::Config>;
}

impl<'a> IntoConfig for &'a str {
    fn into_config(self) -> tiberius::Result<tiberius::Config> {
        tiberius::Config::from_ado_string(self)
    }
}

impl IntoConfig for tiberius::Config {
    fn into_config(self) -> tiberius::Result<tiberius::Config> {
        Ok(self)
    }
}

/// Implements `bb8::ManageConnection`
pub struct ConnectionManager {
    config: tiberius::Config,
    modify_tcp_stream:
        Box<dyn Fn(&tokio::net::TcpStream) -> tokio::io::Result<()> + Send + Sync + 'static>,
}

impl ConnectionManager {
    /// Create a new `ConnectionManager`
    pub fn new(config: tiberius::Config) -> Self {
        Self {
            config,
            modify_tcp_stream: Box::new(|tcp_stream| tcp_stream.set_nodelay(true)),
        }
    }

    /// Build a `ConnectionManager` from e.g. an ADO string
    pub fn build<I: IntoConfig>(config: I) -> Result<Self, Error> {
        Ok(config.into_config().map(Self::new)?)
    }
}

/// The connection type
pub type Client = tiberius::Client<tokio_util::compat::Compat<tokio::net::TcpStream>>;

impl ConnectionManager {
    /// Perform some configuration on the TCP stream when generating connections
    pub fn with_modify_tcp_stream<F>(mut self, f: F) -> Self
    where
        F: Fn(&tokio::net::TcpStream) -> tokio::io::Result<()> + Send + Sync + 'static,
    {
        self.modify_tcp_stream = Box::new(f);
        self
    }

    pub(crate) async fn connect_inner(&self) -> Result<Client, Error> {
        use tokio::net::TcpStream;
        use tokio_util::compat::TokioAsyncWriteCompatExt; //Tokio02AsyncWriteCompatExt;

        let tcp = TcpStream::connect(self.config.get_addr()).await?;

        (self.modify_tcp_stream)(&tcp)?;

        let client = tiberius::Client::connect(self.config.clone(), tcp.compat_write()).await?;

        Ok(client)
    }
}

#[async_trait::async_trait]
impl bb8::ManageConnection for ConnectionManager {
    type Connection = Client;
    type Error = Error;

    async fn connect(&self) -> Result<Self::Connection, Self::Error> {
        Ok(self.connect_inner().await?)
    }

    async fn is_valid<'a, 'b, 'c>(
        &'a self,
        conn: &'b mut bb8::PooledConnection<'c, Self>,
    ) -> Result<(), Self::Error> {
        conn.simple_query("SELECT 1").await?;
        Ok(())
    }

    fn has_broken(&self, _conn: &mut Self::Connection) -> bool {
        false
    }
}

另外可以用来连接SQL Server的是odbc和sqlx, odbc不能await, sqlx 0.5.1中的sql server驱动还没有完全实现。期待sqlx的完整实现,未来可能是一个不错的选择,能够用一套代码连接多种数据库。

1 共 1 条评论, 1 页