Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Please add support for vector parameters #133

Closed
ivanceras opened this issue Jun 22, 2015 · 11 comments
Closed

Please add support for vector parameters #133

ivanceras opened this issue Jun 22, 2015 · 11 comments

Comments

@ivanceras
Copy link

I'm building an ORM on top of your library, but I haven't figured out, how to convert vector to a fixed sized array in rust dynamically.

The query parameters

conn.execute("INSERT INTO person (name, data) VALUES ($1, $2)",
                 &[&me.name, &me.data]).unwrap();

would be much easier to manipulate this way

let mut param = vec![&me.name, &me.data];
param.push("more info here");
conn.execute("INSERT INTO person (name, data, info) VALUES ($1, $2, $3)",
                 &param).unwrap();
@sfackler
Copy link
Owner

The syntax in your second example should work. &v where v is a Vec<T> will coerce into a &[T]. Here's an example with Debug standing in for ToSql: http://is.gd/3r9PF6

@ivanceras
Copy link
Author

I tried supplying the param as a vector slice, but it seemed not coerced to into a &[&ToSql] type.

            let sql = "INSERT INTO person (name, data) VALUES ($1, $2)".to_string();
            let me = Person {
                    id: 0,
                    name: "Steven".to_string(),
                    data: None
                };

            let mut param = vec![&me.name];
            param.push(&me.data);
            conn.execute(&sql, &param).unwrap();

            let stmt = conn.prepare("SELECT id, name, data FROM person").unwrap();
            for row in stmt.query(&[]).unwrap() {
                let person = Person {
                    id: row.get(0),
                    name: row.get(1),
                    data: row.get(2)
                };
                println!("Found person {}", person.name);
            }

I got these error

examples/test_person.rs:42:32: 42:38 error: mismatched types:
 expected `&[&postgres::types::ToSql]`,
    found `&collections::vec::Vec<&collections::string::String>`
(expected slice,
    found struct `collections::vec::Vec`) [E0308]
examples/test_person.rs:42             conn.execute(&sql, &param).unwrap();
                                                          ^~~~~~

I've tested in both rust 1.2.0 and rust 1.0.0 with the same error result

rustc -V
rustc 1.2.0-nightly (0cc99f9cc 2015-05-17) (built 2015-05-18)
rustc -V
rustc 1.0.0 (a59de37e9 2015-05-13) (built 2015-05-14)

@ivanceras
Copy link
Author

I found a way to work around this by explicitly casting the member elements of the vector to &ToSql type.

let sql = "INSERT INTO person (name, data) VALUES ($1, $2)".to_string();
            let me = Person {
                    id: 0,
                    name: "Steven".to_string(),
                    data: None
                };
            let name:&ToSql = &me.name;
            let data:&ToSql = &me.data;
            let mut param = vec![name, data];

            conn.execute(&sql, &param).unwrap();

            let stmt = conn.prepare("SELECT id, name, data FROM person").unwrap();
            for row in stmt.query(&[]).unwrap() {
                let person = Person {
                    id: row.get(0),
                    name: row.get(1),
                    data: row.get(2)
                };
                println!("Found person {}", person.name);
            }

There seemed to be unnecessary code in explicit conversion, but it works. I'm gonna be using this technique, until something more elegant comes up.

@sfackler
Copy link
Owner

You should also be able to specify the type of just the vector, like in this example: http://is.gd/3r9PF6

@ivanceras
Copy link
Author

Yeah, I just realized that it would also work if explicitly the type is specified upon declaration. I feel soo stupid.
Leaving the code here for future references

let conn = pg.conn;
            let sql = "INSERT INTO person (name, data) VALUES ($1, $2)".to_string();
            let me = Person {
                    id: 0,
                    name: "Steven".to_string(),
                    data: None
                };
            let mut param:Vec<&ToSql> = vec![&me.name];
            param.push(&me.data);

            conn.execute(&sql, &param).unwrap();

            let stmt = conn.prepare("SELECT id, name, data FROM person").unwrap();
            for row in stmt.query(&[]).unwrap() {
                let person = Person {
                    id: row.get(0),
                    name: row.get(1),
                    data: row.get(2)
                };
                println!("Found person {}", person.name);
            }

@sfackler
Copy link
Owner

Not a problem :)

@kaibyao
Copy link
Contributor

kaibyao commented Jan 21, 2019

Just wanted to say that I struggled with this for more than a few days before I found this. Can I make a PR with this as a usage example in the docs?

@dbettin
Copy link

dbettin commented Jan 10, 2020

@sfackler Hoping you can help here. I am trying to create a temp table with multiple values for an insert. I am struggling to understand how to dynamically pass in the values.

Here is the error I am receiving:

.execute(temp_table, temp_table_values)
                                  ^^^^^^^^^^^^^^^^^ expected trait `postgres_types::ToSql + std::marker::Sync`, found trait `postgres_types::ToSql`
   
= note: expected type `&[&dyn postgres_types::ToSql + std::marker::Sync]`
             found type `&[&dyn postgres_types::ToSql]`

@sfackler
Copy link
Owner

If you have a&[&dyn ToSql] somewhere, you need to change that to &[&dyn ToSql + Sync].

@isosphere
Copy link

isosphere commented Jul 17, 2020

In case any other rust newbies come here looking for clues, I had the above problem re: the Sync trait, and here's what worked for me (as @sfackler suggested):

let params: Vec<&(dyn ToSql + Sync)> = Vec::new();
                        
for column in set_of_things{
    params.push(&column);
}
params.push(&something_different);

 client.execute(&statement, &params[..]).unwrap();

As a newbie, this seems like some kind of witchcraft that I don't quite understand. For example, the need to provide params as a slice of all elements was a head-scratcher. The friendly compiler got me there in the end!

@ctron
Copy link

ctron commented May 4, 2021

If you are using the query_raw method (which seems to be the only one returning a stream), then passing in a slice won't work, as it expects an iterator. However, you can refine the example from @isosphere and mix it with the slice_iter method:

fn my_query() {
  let mut params: Vec<&(dyn ToSql + Sync)> = Vec::new();
                          
  for column in set_of_things{
      params.push(&column);
  }
  params.push(&something_different);

  client
    .query_raw(&statement, slice_iter(&params[..]))
}

fn slice_iter<'a>(
    s: &'a [&'a (dyn ToSql + Sync)],
) -> impl ExactSizeIterator<Item = &'a dyn ToSql> + 'a {
    s.iter().map(|s| *s as _)
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants