【问题标题】:How to execute a collection of statements in Tiberius?如何在 Tiberius 中执行一组语句?
【发布时间】:2020-02-19 19:25:17
【问题描述】:

我不知道如何使用 Tiberius 遍历集合并一一执行语句。

我当前的代码如下所示(简化):

use futures::Future;
use futures_state_stream::StateStream;
use tokio::executor::current_thread;
use tiberius::SqlConnection;

fn find_files(files: &mut Vec<String>) {
    files.push(String::from("file1.txt"));
    files.push(String::from("file2.txt"));
    files.push(String::from("file3.txt"));
}

fn main() {
    let mut files: Vec<String> = Vec::new();
    find_files(&mut files);

    let future = SqlConnection::connect(CONN_STR)
        .and_then(|conn| {
            conn.simple_exec("CREATE TABLE db.dbo.[Filenames] ( [Spalte 0] varchar(80) );")
        })
        .and_then(|(_, conn)| {
            for k in files.iter() {
                let sql = format!("INSERT INTO db.dbo.Filenames ([Spalte 0]) VALUES ('{}')", k);
                &conn.simple_exec(sql);
            }
            Ok(())
        });

    current_thread::block_on_all(future).unwrap();
}

我收到以下错误消息

error[E0382]: use of moved value: `conn`
  --> src/main.rs:23:18
   |
20 |         .and_then(|(_, conn)| {
   |                        ---- move occurs because `conn` has type `tiberius::SqlConnection<std::boxed::Box<dyn tiberius::BoxableIo>>`, which does not implement the `Copy` trait
...
23 |                 &conn.simple_exec(sql);
   |                  ^^^^ value moved here, in previous iteration of loop

我是 Rust 新手,但我知道使用 conn 变量有问题,但没有任何效果。

【问题讨论】:

    标签: sql-server database rust


    【解决方案1】:

    这里实际上有两个问题:

    1. 头问题:如何使用tiberius执行多个顺序语句?
    2. 关于为什么错误消息来自特定代码位的具体问题。 我将分别回答。

    多个语句

    给猫剥皮的方法有很多种。在 TDS(Tiberius 正在实现的底层协议)中,可以在单个命令中执行多个语句。它们只需要使用分号分隔即可。这种执行的响应在 Tiberius 中代表了一个未来流,每个语句一个。 因此,如果您的语句链不是太大而无法放入一个命令中,只需构建一个字符串并将其发送过来:

    fn main() { 
        let mut files: Vec<String> = Vec::new(); 
        find_files(&mut files); 
        let stmts = vec![ 
            String::from( 
                "CREATE TABLE db.dbo.[Filenames] ( [Spalte 0] varchar(80) )")] 
            .into_iter() 
            .chain(files.iter().map(|k| 
                format!("INSERT INTO db.dbo.Filenames ([Spalte 0]) VALUES ('{}')", k))) 
            .collect::<Vec<_>>() 
            .join(";"); 
    
        let future 
            = SqlConnection::connect(std::env::var("CONN_STR").unwrap().as_str()) 
                .and_then(|conn| 
                    conn.simple_exec(stmts) 
                        .into_stream() 
                        .and_then(|future| future) 
                        .for_each(|_| Ok(()))); 
    
        current_thread::block_on_all(future).unwrap(); 
    } 
    

    该示例中有一些简单的样板。

    • simple_exec 返回一个 ExecResult,它是单个语句未来结果的包装器。调用 `into_stream() 会提供这些期货的流。
    • 需要强制执行结果流,一种方法是调用and_then,它会等待每个未来并对其进行处理。
    • 我们实际上并不关心这里的结果,所以我们只做一个 noop for_each

    但是,假设有 很多 个语句,超过了单个 TDS 命令的容量,那么就需要单独发出它们(另一种情况是语句本身依赖于较早的)。该问题的一个版本已在 How do I iterate over a Vec of functions returning Futures in Rust? 中解决。

    最后,您的具体错误是什么? connsimple_exec使用了,所以它以后不能使用,这就是错误告诉你的。如果您想在执行完成后使用连接,您必须使用它返回的Future,它包装了变异的连接。我按照上面的链接,以一种方式做到这一点。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-02-13
      • 1970-01-01
      • 1970-01-01
      • 2011-03-09
      相关资源
      最近更新 更多