Following "getting started" diesel tutorial, but with sqlite, causes - sqlite

I'm trying to follow: https://diesel.rs/guides/getting-started but I'm using:
echo DATABASE_URL=/tmp/diesel_demo.sqlite > .env
instead of a Postgres database.
I've changed all occurrences of Pg to Sqlite, and SERIAL to INT, but get the following error:
error[E0277]: the trait bound `i32: FromSql<diesel::sql_types::Nullable<diesel::sql_types::Integer>, Sqlite>` is not satisfied
--> src/bin/show_posts.rs:14:10
|
14 | .load::<Post>(&connection)
| ^^^^ the trait `FromSql<diesel::sql_types::Nullable<diesel::sql_types::Integer>, Sqlite>` is not implemented for `i32`
How to get a result set where field value == row number?
show_posts.rs:
extern crate diesel_demo;
extern crate diesel;
use self::diesel_demo::*;
use self::models::*;
use self::diesel::prelude::*;
fn main() {
use diesel_demo::schema::posts::dsl::*;
let connection = establish_connection();
let results = posts.filter(published.eq(true))
.limit(5)
.load::<Post>(&connection)
.expect("Error loading posts");
println!("Displaying {} posts", results.len());
for post in results {
println!("{}", post.title);
println!("----------\n");
println!("{}", post.body);
}
}
up.sql:
CREATE TABLE posts (
id INTEGER PRIMARY KEY,
title VARCHAR NOT NULL,
body TEXT NOT NULL,
published BOOLEAN NOT NULL DEFAULT 'f'
)
models.rs (autogenerated):
#[derive(Queryable)]
pub struct Post {
pub id: i32,
pub title: String,
pub body: String,
pub published: bool,
}
I don't understand why Diesel expects id to be Nullable.

Adding NOT NULL to the id field in up.sql fixed it.

Related

What is the best way to store an enum in a database in rusqlite?

If I have a simple struct, one attribute of which contains a simple enum, how can I best store examples of this struct with their enumerations in the rusqlite database? Something like:
use rusqlite::{params, Connection, Result};
enum Sex{
Unknown,
Male,
Female,
}
struct Person{
name: String,
sex: Sex
}
fn main() -> Result<()> {
let conn = Connection::open_in_memory()?;
conn.execute(
"CREATE TABLE people(
name TEXT NOT NULL,
sex TEXT NOT NULL
)",
(), // empty list of parameters.
)?;
let person_01 = Person{
name: String::from("Adam"),
sex: Sex::Male
};
conn.execute(
"INSERT INTO people (name, sex) VALUES (?1, ?2)",
(&person_01.name, &person_01.sex),
)?;
Ok(())
}
The problem is that sqlite only allows data of restricted types (NULL, INTEGER, REAL, TEXT), trying to use TEXT here for the enum gives the following error:
error[E0277]: the trait bound `Sex: ToSql` is not satisfied
--> src/main.rs:33:9
|
31 | conn.execute(
| ------- required by a bound introduced by this call
32 | "INSERT INTO tasklist (name, sex) VALUES (?1, ?2)",
33 | (&person_01.name, &person_01.sex),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `ToSql` is not implemented for `Sex`
This error makes sense, but what is the best way to implement this? Cast the enum to int? I read here that this is "removes the guarantee that values always represent the variants of the enum", which I agree with. It would be nicer to match the string.
I have tried to do this using strum, which allows me to add to_str & from_str to the enum, allowing it to be added to the database like so:
#[derive(strum_macros::Display, strum_macros::EnumString, Debug)]
enum Sex{
Unknown,
Male,
Female,
}
...
conn.execute(
"INSERT INTO people (name, sex) VALUES (?1, ?2)",
(&person_01.name, &person_01.sex.to_string())
)?;
and retrieved like so:
let mut stmt = conn.prepare("SELECT name, sex FROM people")?;
let person_itr = stmt.query_map([], |row|{
Ok(
Person{
name: row.get(0)?,
sex: Sex::from_str(String::as_str(&row.get(1)?)).unwrap(),
}
)
});
but this feels messy. Is there a better way?
I have seen here people manually implementing FromSqlRow for the enum, but is there a better (quicker) way?
The right way to handle this is to implement ToSql and FromSql directly on your enum. This will make using it substantially more ergonomic, and possibly more efficient since you don't first have to convert to a type with an allocation, like String.
It also means the conversion to/from a string doesn't "infect" every interaction you have with the database; the conversion becomes automatic. So, while there is a bit more boilerplate, it will pay off every time you use this type in conjunction with the database.
impl ToSql for Sex {
fn to_sql(&self) -> rusqlite::Result<ToSqlOutput<'_>> {
Ok(self.to_string().into())
}
}
impl FromSql for Sex {
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
value.as_str()?.parse()
.map_err(|e| FromSqlError::Other(Box::new(e)))
}
}
Now you can just do this when converting to Person:
sex: row.get(1)?,
Note that FromSqlRow is a trait specific to a postgres client; rusqlite has no such trait. If you wanted to, you could create a factory method on Person that constructs from a Row. That's up to you.

How to eliminate partial move when calling map on an iterator

I have a simple (I thought it should be) task to map values contained in a Vec and produce another Vec:
#[derive(Clone)]
struct Value(u32);
#[derive(Clone)]
struct Id(u32);
struct ValuesInfo {
values: Vec<Value>,
name: String,
id: Id
}
struct ValueInfo{
value: Value,
name: String,
id: Id
}
fn extend_values(v: Vec<ValuesInfo>) -> Vec<Vec<ValueInfo>> {
v.into_iter().map(|values_info|{
values_info.values.into_iter().map(|value|{
ValueInfo{
value,
name: values_info.name.clone(),
id: values_info.id.clone()
}
}).collect::<Vec<ValueInfo>>()
}).collect::<Vec<Vec<ValueInfo>>>()
}
Playground permalink
Here I have a partial move error looking as
Compiling playground v0.0.1 (/playground)
error[E0382]: borrow of moved value: `values_info`
--> src/lib.rs:20:44
|
20 | values_info.values.into_iter().map(|value|{
| ----------- ^^^^^^^ value borrowed here after partial move
| |
| `values_info.values` moved due to this method call
...
23 | name: values_info.name.clone(),
| ----------- borrow occurs due to use in closure
|
note: this function consumes the receiver `self` by taking ownership of it, which moves `values_info.values`
= note: move occurs because `values_info.values` has type `std::vec::Vec<Value>`, which does not implement the `Copy` trait
error: aborting due to previous error
I need this partial move because this is what the task is about. Is there any workaround to solve the error?
In the 2018 edition of Rust, closures always capture entire variables by name. So the closure passed to the inner map would take a reference to values_info, which is not valid because values_info has already been partially moved (even though the closure does not need access to the part that was moved).
Rust 2021+
Since Rust 2021, captures borrow (or move) only the minimal set of fields required in the body of the closure. This makes the original code work as you expected¹, so you can make the error go away by simply changing the playground edition to 2021. See the edition guide for more.
Rust 2015 & 2018
In previous editions, you can do it manually: destructure the ValuesInfo first, and only capture name and id inside the closure. You can destructure the ValuesInfo as soon as you get it, in the argument list of the outer closure:
fn extend_values(v: Vec<ValuesInfo>) -> Vec<Vec<ValueInfo>> {
v.into_iter()
.map(|ValuesInfo { values, name, id }| {
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ like `let ValuesInfo { values, name, id } = values_info;`
values
.into_iter()
.map(|value| ValueInfo {
value,
name: name.clone(),
id: id.clone(),
})
.collect()
})
.collect()
}
See also
How to use struct self in member method closure
HashMap borrow issue when trying to implement find or insert is solved using a similar trick.
¹ Unless ValuesInfo implements Drop, which makes any destructuring or partial move unsound.
Naming values_info in the closure will borrow it
as a whole although it is already partially moved
(as told by the error message).
You should borrow the members you need beforehand.
let name=&values_info.name;
let id=&values_info.id;
values_info.values.into_iter().map(|value|{
ValueInfo{
value,
name: name.clone(),
id: id.clone(),
}
#[derive(Clone)] //you could derive Copy. u32 is generally cheap to copy
struct Value(u32);
#[derive(Clone)] //you could derive Copy
struct Id(u32);
struct ValuesInfo {
values: Vec<Value>,
name: String,
id: Id
}
struct ValueInfo{
value: Value,
name: String,
id: Id
}
//No need to consume v. Not sure if it will come in handy
fn extend_values(v: &Vec<ValuesInfo>) -> Vec<Vec<ValueInfo>> {
//Use into_iter only if you need the ownership of the content of the array
// which I don't think you need because you are cloning every value of ValueInfo
//except for value which is already cheep to copy/clone
v.iter().map(|values_info|{
values_info.values.iter().map(|value|{
ValueInfo{
value: value.clone(),
name: values_info.name.clone(),
id: values_info.id.clone()
}
}).collect::<Vec<ValueInfo>>()
}).collect::<Vec<Vec<ValueInfo>>>()
}
That said, if you really don't want to copy/clone Value you need to clone name and id before hand. The compiler stops you from using values_info.name.clone() because the function into_iter already consumed values_info.
The code would look something like this if you really don't want to copy Value
#[derive(Clone)]
struct Value(u32);
#[derive(Clone)]
struct Id(u32);
struct ValuesInfo {
values: Vec<Value>,
name: String,
id: Id
}
struct ValueInfo{
value: Value,
name: String,
id: Id
}
fn extend_values(v: Vec<ValuesInfo>) -> Vec<Vec<ValueInfo>> {
v.into_iter().map(|values_info|{
let name = values_info.name.clone();
let id = values_info.id.clone();
values_info.values.into_iter().map(
|value| {
ValueInfo{
value,
name: name.clone(),
id: id.clone(),
}
}).collect::<Vec<ValueInfo>>()
}).collect::<Vec<Vec<ValueInfo>>>()
}

How to use i64 with Insertable using Diesel

How do I use i64/u64 with Diesel?
Do I really need to implement the diesel::Expression trait for the primitive type?
Here is my code.
Cargo.toml:
[dependencies]
...
diesel = { version = "1.4.5", features = ["sqlite", "numeric"] }
migration/up.sql:
CREATE TABLE books (
id INTEGER NOT NULL PRIMARY KEY,
size INTEGER NOT NULL
);
schema.rs:
table! {
books (id) {
id -> Integer,
size -> Integer,
}
}
sources:
use crate::schema::books;
#[derive(Insertable, Queryable)]
#[table_name="books"]
pub struct BookRecord {
pub id: Id,
pub size: i64,
}
This gives the following error:
error[E0277]: the trait bound `i64: diesel::Expression` is not satisfied
--> src/lib/models/book.rs:4:10
|
4 | #[derive(Insertable, Queryable)]
| ^^^^^^^^^^ the trait `diesel::Expression` is not implemented for `i64`
|
= note: required because of the requirements on the impl of `diesel::expression::AsExpression<diesel::sql_types::Integer>` for `i64`
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
How do I resolve this error?
i64 corresponds to BigInt and i32 corresponds to Integer. Either change your schema to use BigInt, or change your numbers to i32.

How to get back one row's data in rusqlite?

rustc 1.38.0 (625451e37 2019-09-23)
rusqlite 0.20.0
I'm writing a program where I need to get back the id from the last insertion that sqlite just created.
db.execute("insert into short_names (short_name) values (?1)",params![short]).expect("db insert fail");
let id = db.execute("SELECT id FROM short_names WHERE short_name = '?1';",params![&short]).query(NO_PARAMS).expect("get record id fail");
let receiver = db.prepare("SELECT id FROM short_names WHERE short_name = "+short+";").expect("");
let id = receiver.query(NO_PARAMS).expect("");
println!("{:?}",id);
What I should be getting back is the id value sqlite automatically assigned with AUTOINCREMENT.
I'm getting this compiler Error:
error[E0599]: no method named `query` found for type `std::result::Result<usize, rusqlite::Error>` in the current scope
--> src/main.rs:91:100
|
91 | let id = db.execute("SELECT id FROM short_names WHERE short_name = '?1';",params![&short]).query(NO_PARAMS).expect("get record id fail");
| ^^^^^
error[E0369]: binary operation `+` cannot be applied to type `&str`
--> src/main.rs:94:83
|
94 | let receiver = db.prepare("SELECT id FROM short_names WHERE short_name = "+short+";").expect("");
| ------------------------------------------------^----- std::string::String
| | |
| | `+` cannot be used to concatenate a `&str` with a `String`
| &str
help: `to_owned()` can be used to create an owned `String` from a string reference. String concatenation appends the string on the right to the string on the left and may require reallocation. This requires ownership of the string on the left
|
94 | let receiver = db.prepare("SELECT id FROM short_names WHERE short_name = ".to_owned()+&short+";").expect("");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^
error[E0277]: `rusqlite::Rows<'_>` doesn't implement `std::fmt::Debug`
--> src/main.rs:96:25
|
96 | println!("{:?}",id);
| ^^ `rusqlite::Rows<'_>` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug`
|
= help: the trait `std::fmt::Debug` is not implemented for `rusqlite::Rows<'_>`
= note: required by `std::fmt::Debug::fmt`
Line 94: I understand that rust's String is not the right type for the execute call, but I'm not sure what to do instead.
I suspect what needs to happen is the short_names table needs to be pulled form the database and then from the rust representation of the table for get the id that matches the short I'm trying to work with. I've been going off this example as a jumping off point, but It's dereferenced it's usefulness. The program I'm writing calls another program and then babysits it while this other program runs. To reduce over head I'm trying to not use OOP for this current program.
How should I structure my request to the database to get by the id I need?
Okay. First off, we are going to use a struct, because, unlike in Java, it is literally equivalent to not using one in this case, except that you gain in being able to keep things tidy.
You're trying to emulate Connection::last_insert_rowid(), which isn't a terribly smart thing to do, particularly if you are not in a transaction. We're also going to clear this up for you in a nice and neat fashion:
use rusqlite::{Connection};
pub struct ShortName {
pub id: i64,
pub name: String
}
pub fn insert_shortname(db: &Connection, name: &str) -> Result<ShortName, rusqlite::Error> {
let mut rtn = ShortName {
id: 0,
name: name.to_string()
};
db.execute("insert into short_names (short_name) values (?)",&[name])?;
rtn.id = db.last_insert_rowid();
Ok(rtn)
}
You can convince yourself that it works with this test:
#[test]
fn it_works() {
let conn = Connection::open_in_memory().expect("Could not test: DB not created");
let input:Vec<bool> = vec![];
conn.execute("CREATE TABLE short_names (id INTEGER PRIMARY KEY AUTOINCREMENT, short_name TEXT NOT NULL)", input).expect("Creation failure");
let output = insert_shortname(&conn, "Fred").expect("Insert failure");
assert_eq!(output.id, 1);
}
In rusqlite execute does not return a value. To return a value from a sqlite operation you need to use prepare and a variant of query. While much of Rust allows you to leave type up to the compiler, for rusqite you need to give the receiving variable a type.
There is not currently a way in rusqlite to take a single row out of a query. The type of rows is not a type iterator, so you need to progress over it with a while loop, that will progress based on the error type of rows. After the loop runs once it will return that there are no other row in rows and exit; if there is only one row from the query.
You can use query_named to modify the sql query your sanding. Using the named_params!{} macro will allow you to use a String to send information to the command.
use rusqlite::*;
fn main() {
let short = "lookup".to_string(); // example of a string you might use
let id:i64 = 0;
{ // open for db work
let db = Connection::open("YourDB.db").expect("db conn fail");
let mut receiver = db
.prepare("SELECT * FROM short_names WHERE short_name = :short;")
.expect("receiver failed");
let mut rows = receiver
.query_named(named_params!{ ":short": short })
.expect("rows failed");
while let Some(row) = rows.next().expect("while row failed") {
id=row.get(0).expect("get row failed");
}
} // close db work
println!("{}", id);
}
In the above example, we open a scope with {} around the database transaction, this will automatically close the db when it goes out of scope. Notice that we create our db connection and do all our work with the database solely inside the {}. This allows us to skip closing the db with the explicate command and is done by inference taken by the compiler from the scope: {}. The variables short and id, created in the scope of main(), are still available to the db scope and the rest of the scope of main(). While id is not assigned until the db scope, but it's defined outside of the scope, the scope of main, so that is where id's lifetime begins. id does not need to be mutable because it's only assigned once, if there is in fact only one row to retrieve, the while loop will only assign it once. Otherwise, if the database does not behave as expected this will result in an error.

How to convert DateTime::now() into NaiveDateTime?

I'm using Diesel and chrono. In my model I have a field of type NaiveDateTime which contains the now(). However, NaiveDateTime doesn't have the function now() or a similar one whereas DateTime does:
Utc::now()
How can I convert Utc::now() into NaiveDateTime?
Utc::now() returns a DateTime<Utc>. You could click into the documentation of DateTime<T> and search for NaiveDateTime. You should find that there are two methods that will return a NaiveDateTime:
fn naive_utc(&self) -> NaiveDateTime
  Returns a view to the naive UTC datetime.
fn naive_local(&self) -> NaiveDateTime
  Returns a view to the naive local datetime.
For instance, if you need the timestamp in UTC:
let naive_date_time = Utc::now().naive_utc();
Note that since you are using diesel, you could use diesel::dsl::now instead, which will evaluate to CURRENT_TIMESTAMP on the SQL side.
//! ```cargo
//! [dependencies]
//! diesel = { version = "1", features = ["sqlite"] }
//! ```
#[macro_use]
extern crate diesel;
use diesel::prelude::*;
use diesel::dsl;
table! {
posts (id) {
id -> Integer,
content -> Text,
published -> Timestamp,
}
}
fn main() {
let conn = SqliteConnection::establish("test.db")
.expect("Cannot open database");
diesel::insert_into(posts::table)
.values((
posts::content.eq("hello"),
posts::published.eq(dsl::now), // <------------------
))
.execute(&conn)
.expect("Insertion failed");
}

Resources