0

I could not figure out how to iterate over a collection and execute statements one by one with Tiberius.

My current code looks like this (simplified):

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();
}

I got the following error message

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

I'm new to Rust but I know there is something wrong with the use of the conn variable but nothing works.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Serbroda
  • 111
  • 9

1 Answers1

0

There are actual two questions here:

  1. The header question: how to perform multiple sequential statements using tiberius?
  2. The specific question concerning why an error message comes from a specific bit of code. I will answer them separately.

Multiple statements

There are many ways to skin a cat. In TDS (the underlying protocol Tiberius is implementing) there is the possibility to execute several statements in a single command. They just need to be delimited by using semicolon. The response from such an execution is in Tiberius represented a stream of futures, one for each statement. So if your chain of statements is not too big to fit into one command, just build one string and send it over:

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(); 
} 

There is some simple boilerplate in that example.

  • simple_exec returns an ExecResult, a wrapper around the individual statement's future results. Calling `into_stream() on that provides a stream of those futures.
  • That stream of results need to be forced to be carried out, one way of doing that is to call and_then, which awaits each future and does something with it.
  • We don't actually care about the results here, so we just do a noop for_each.

But, say that there is a lot of statements, more than can fit in a single TDS command, then there is a need to issue them separately (another case is when the statements themselves depend on earlier ones). A version of that problem is solved in How do I iterate over a Vec of functions returning Futures in Rust? .

Then finally, what is your specific error? Well conn is consumed by simple_exec, so it cannot be used afterwards, that is what the error tells you. If you want to use the connection after that execution is done you have to use the Future it returns, which is wrapping the mutated connection. I defer to the link above, on one way to do that.