3

Here is the code I use to insert data in a Postgres database using the postgres crate (not present on Rust Playground unfortunately):

With the following Cargo.toml:

[package]
name = "suff-import"
version = "0.1.0"
authors = ["greg"]

[dependencies]
csv = "1"
postgres = "0.15"
uuid = "0.7"

File main.rs:

extern crate csv;
extern crate postgres;
extern crate uuid;

use uuid::Uuid;
use postgres::{Connection, TlsMode};
use std::error::Error;
use std::io;
use csv::StringRecord;

struct Stuff {
    stuff_id: Uuid,
    created_at: String,
    description: String,
    is_something: bool,
}

impl Stuff {
    fn from_string_record(record: StringRecord) -> Stuff {
        Stuff {
            stuff_id: Uuid::parse_str(record.get(0).unwrap()).unwrap(),
            created_at: record.get(1).unwrap().to_string(),
            description: record.get(2).unwrap().to_string(),
            is_something: record.get(3).unwrap().to_string().parse::<i32>().unwrap() == 2,
        }
    }
}

fn save_row(dbcon: &Connection, stuff: Stuff) -> Result<(), Box<Error>> {
    dbcon.execute(
        "insert into public.stuff (stuff_id, created_at, description, is_something) values ($1::uuid, $2, $3, $4)",
        &[&format!("{}", &stuff.stuff_id).as_str(), &stuff.created_at, &stuff.description, &stuff.is_something]
    )?;
    Ok(())
}


fn import() -> Result<(), Box<Error>> {
    let mut reader = csv::Reader::from_reader(io::stdin());
    let dbcon = Connection::connect("postgres://gregoire@10.129.198.251/gregoire", TlsMode::None).unwrap();

    for result in reader.records() {
        let record = result?;
        println!(".");
        save_row(&dbcon, Stuff::from_string_record(record))?;
    }

    Ok(())
}

fn main() {
    if let Err(error) = import() {
        println!("There were some errors: {}", error);
        std::process::exit(1);
    }
}

The program compiles, but it exits when run with the error message:

./target/debug/suff-import <<EOF
stuff_id,created_at,description,is_something
5252fff5-d04f-4e0f-8d3e-27da489cf40c,"2019-03-15 16:39:32","This is a description",1
EOF
.
There were some errors: type conversion error: cannot convert to or from a Postgres value of type `uuid`

I tested turning the UUID into a &str with the format! macro as Postgres should implicitly do the conversion to UUID, but it did not work (same error message). I then added an explicit $1::uuid in the Postgres query but the problem still occurs.

greg
  • 3,354
  • 1
  • 24
  • 35
  • I'd say, either use `$1::uuid` and `&stuff.stuff_id` (no conversion to `&str`) or use `$1` (no `::uuid`) and `&format!("{}", &stuff.stuff_id).as_str()`. As is you're telling postgres to expect a UUID object, but you're passing a string… – Jmb Mar 18 '19 at 13:52
  • @Jmb, the `$1::uuid` is a cast operator on the first argument, if the first argument is a string, it will be cast into a `uuid`. On the rust part, the array used to pass query parameters is an array of `&str`. I will follow @shepmaster instruction to provide a better question. – greg Mar 18 '19 at 16:18

1 Answers1

3

The crate page states:

Optional features

UUID type

UUID support is provided optionally by the with-uuid feature, which adds ToSql and FromSql implementations for uuid's Uuid type. Requires uuid version 0.5.

You haven't specified the feature, and you are using an incompatible version of uuid.

Cargo.toml

[package]
name = "repro"
version = "0.1.0"
edition = "2018"

[dependencies]
postgres = { version = "0.15.2", features = ["with-uuid"] }
uuid = "0.5"

Database setup

CREATE TABLE junk (id uuid);

Code

use postgres::{Connection, TlsMode};
use std::error::Error;
use uuid::Uuid;

fn main() -> Result<(), Box<Error>> {
    let conn = Connection::connect(
        "postgresql://shep@localhost:5432/stackoverflow",
        TlsMode::None,
    )
    .unwrap();

    let stuff_id = Uuid::default();

    conn.execute(
        "insert into public.junk (id) values ($1)",
        &[&stuff_id],
    )?;

    Ok(())
}

See also:

Community
  • 1
  • 1
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366