I have been taking a look at Rust's Diesel ORM today by following along on this walk-through, and I can't get a Timestamp
to work.
Cargo.toml
[dependencies] diesel = { version = "0.6.2", features = ["chrono"] } diesel_codegen = { version = "0.6.2", default-features = false, features = ["nightly", "postgres"] } dotenv = "0.8.0" dotenv_macros = "0.8.0"
models.rs
#[derive(Queryable)] pub struct Author { pub id: i32, pub first_name: String, pub last_name: String, pub email: String } pub struct Post { pub id: i32, pub author: Author, pub title: String, pub body: String, pub published: bool, pub created: Timestamp, pub updated: Timestamp }
(I read that there's a diesel::types::Timestamp
type)
lib.rs
#![feature(custom_derive, custom_attribute, plugin)] #![plugin(diesel_codegen, dotenv_macros)] #[macro_use] extern crate diesel; extern crate dotenv; pub mod schema; pub mod models; use diesel::prelude::*; use diesel::types::Timestamp; use diesel::pg::PgConnection; use dotenv::dotenv; use std::env; pub fn establish_connection() -> PgConnection { dotenv().ok(); let database_url = env::var("DATABASE_URL"). expect("DATABASE_URL must be set"); PgConnection::establish(&database_url). expect(&format!("Error connecting to {}", database_url)) }
But these are the errors I get when I try to use it:
<diesel macros>:5:1: 5:71 note: in this expansion of table_body! (defined in <diesel macros>) src/schema.rs:1:1: 1:40 note: in this expansion of table! (defined in <diesel macros>) src/schema.rs:1:1: 1:40 note: in this expansion of infer_schema! (defined in src/lib.rs) src/lib.rs:1:1: 1:1 help: run `rustc --explain E0412` to see a detailed explanation src/lib.rs:1:1: 1:1 help: no candidates by the name of `Timestamptz` found in your project; maybe you misspelled the name or forgot to import an external crate? src/lib.rs:1:1: 1:1 error: type name `Timestamptz` is undefined or not in scope [E0412] src/lib.rs:1 #![feature(custom_derive, custom_attribute, plugin)] ... <diesel macros>:38:1: 38:47 note: in this expansion of column! (defined in <diesel macros>) <diesel macros>:5:1: 5:71 note: in this expansion of table_body! (defined in <diesel macros>) src/schema.rs:1:1: 1:40 note: in this expansion of table! (defined in <diesel macros>) src/schema.rs:1:1: 1:40 note: in this expansion of infer_schema! (defined in src/lib.rs) src/lib.rs:1:1: 1:1 help: run `rustc --explain E0412` to see a detailed explanation src/lib.rs:1:1: 1:1 help: no candidates by the name of `Timestamptz` found in your project; maybe you misspelled the name or forgot to import an external crate? src/models.rs:16:18: 16:27 error: type name `Timestamp` is undefined or not in scope [E0412] src/models.rs:16 pub created: Timestamp, ^~~~~~~~~ src/models.rs:16:18: 16:27 help: run `rustc --explain E0412` to see a detailed explanation src/models.rs:16:18: 16:27 help: you can import it into scope: `use diesel::types::Timestamp;`. src/models.rs:17:18: 17:27 error: type name `Timestamp` is undefined or not in scope [E0412] src/models.rs:17 pub updated: Timestamp ^~~~~~~~~
It looks like the first error, Timestamptz
is a result of infer_schema
not knowing how to interpret that Postgresql type, which is already in the table. As for the second, I thought perhaps if explicitly imported that Timestamp
type, I could create a Post
struct with it.
Is there something obvious that I am doing wrong here?
As an aside, I am pretty new to Rust and Diesel uses a fair bit of code-generation, so it's easy to get lost, but I thought this should be a straightforward thing to accomplish.
Edit:
I used timestamp with time zone
to create the table, and it looks like that may not be supported yet:
CREATE TABLE post ( ... created timestamp with time zone NOT NULL, updated timestamp with time zone )
Edit 2:
I changed models.rs to look like the following and got rid of the error about Timestamp
being undefined. I also realized that I needed #[derive(Queryable)]
above each of the structs to be derived. The following compiles fine, but the previous errors with Timestamptz
remain:
use diesel::types::Timestamp; #[derive(Queryable)] pub struct Author { pub id: i32, pub first_name: String, pub last_name: String, pub email: String } #[derive(Queryable)] pub struct Post { pub id: i32, pub author: Author, pub title: String, pub body: String, pub published: bool, pub created: Timestamp, pub updated: Timestamp }
By casting "TimeStamp" to date you throw away the time part of the timestamp, so all values within one day will be considered equal and are returned in random order. It is by accident that the first rows appear in the order you desire. Don't cast to date in the ORDER BY clause if the time part is relevant for sorting.
PostgreSQL provides you with two temporal data types for handling timestamp: timestamp : a timestamp without timezone one. timestamptz : timestamp with a timezone.
Don't use the timestamp type to store timestamps, use timestamptz (also known as timestamp with time zone) instead.
All of the types in diesel::sql_types
are markers to represent various SQL datatypes for your schema. They should never be used in your own structs. What you need is a type which implements diesel::deserialize::FromSql<diesel::sql_types::Timestamp, diesel::pg::Pg>
(docs: FromSql
, Timestamp
, Pg
). There are two types which implement that trait.
The first is std::time::SystemTime
which doesn't require additional dependencies, but doesn't have a ton of capabilities.
The second is chrono::NaiveDateTime
. This is probably the type you want. In order to use it, you'll need to add chrono
to your dependencies, and change the diesel line in Cargo.toml to include the chrono feature, so it'll look something like diesel = { version = "0.7.0", features = ["postgres", "chrono"] }
(Technically there's a third type, which is diesel::data_types::PgTimestamp
but that's almost certainly not what you want, as that struct is just the literal representation of timestamp in the database so other types don't have to worry about raw bytes)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With