Lee Self
Lee Self

Reputation: 13

Why does Rusqlite reject the Option type when executing a query?

I'm trying to insert CSV data into a SQLite database. stripe_id is optional, and so its type is Option<&str>. All the other fields are &str. When I use conn.execute to insert the values, they all insert correctly except for stripe_id, which throws an error saying it expects &str and not Option.

I've searched the docs and Option<T> implements ToSQL, and when I've tried replacing my code with the Rusqlite example that includes an Option value, it throws the same error for the example code.

The relevant struct and query code snippet:

struct Merchant<'a> {
    name: &'a str,
    billing_portal: &'a str,
    billing_period: &'a str,
    stripe_id: Option<&'a str>,
}
conn.execute(
    "INSERT INTO merchants (name, billing_portal, billing_period, stripe_id)
     values (?, ?, ?, ?)",
     &[&merch.name, &merch.billing_portal, &merch.billing_period, &merch.stripe_id]
).expect("Error inserting merchant into database");

The error:

error[E0308]: mismatched types
  --> src/main.rs:38:75
   |
38 |              &[&merch.name, &merch.billing_portal, &merch.billing_period, &merch.stripe_id]
   |                                                                           ^^^^^^^^^^^^^^^^ expected `&str`, found enum `std::option::Option`
   |
   = note: expected reference `&&str`
              found reference `&std::option::Option<&str>`

And the complete code:

extern crate csv;
extern crate rusqlite;

use rusqlite::{Connection, Result};

#[derive(Debug)]
struct Merchant<'a> {
    name: &'a str,
    billing_portal: &'a str,
    billing_period: &'a str,
    stripe_id: Option<&'a str>,
}

fn main() -> Result<()> {
    let conn = Connection::open("data.sqlite")?;

    let mut reader = csv::ReaderBuilder::new()
                                        .has_headers(false)
                                        .from_path("merchants.csv")
                                        .expect("Failed to read csv");
    for record in reader.records() {
        let record = record.unwrap();
        let merch = Merchant {
            name: &record[0],
            billing_portal: &record[3],
            billing_period: &record[4],
            stripe_id: (match &record[5] {
                x if x == "" => None,
                x            => Some(x)
            }),
        };
        println!("{:?}", &merch);

        conn.execute(
            "INSERT INTO merchants (name, billing_portal, billing_period, stripe_id)
             values (?, ?, ?, ?)",
             &[&merch.name, &merch.billing_portal, &merch.billing_period, &merch.stripe_id]
        ).expect("Error inserting merchant into database");

    }

    Ok(())
}

Upvotes: 1

Views: 982

Answers (1)

edwardw
edwardw

Reputation: 13972

Using the rusqlite::params macro solves the problem:

use rusqlite::{params, Connection, Result};

fn main() -> Result<()> {
    // ...

    for record in reader.records() {
        // ...

        conn.execute(
            "INSERT INTO merchants (name, billing_portal, billing_period, stripe_id)
             values (?, ?, ?, ?)",
            params![
                &merch.name,
                &merch.billing_portal,
                &merch.billing_period,
                &merch.stripe_id,
            ],
        )
        .expect("Error inserting merchant into database");
    }

    Ok(())
}

Upvotes: 3

Related Questions