Failed to save array of enums
DylanVerstraete opened this issue · 3 comments
Setup
Versions
- 1.72.0
- 2.1.3
- Postgres 15.4
- MacOs
Feature Flags
- ["postgres", "chrono"]
Problem Description
I'm trying to persist a list of enums on an object. I'm getting a confusing error that I don't understand.
I have following rust code:
#[derive(Debug, Serialize, Deserialize, PartialEq, ToSchema, FromSqlRow, AsExpression, Clone)]
#[diesel(sql_type = crate::postgres::schema::sql_types::Action)]
pub enum Action {
Read,
Write,
Delete,
List,
Admin,
}
// Implement ToSql for the custom enum
impl ToSql<crate::postgres::schema::sql_types::Action, Pg> for Action {
fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
match *self {
Action::Read => out.write_all(b"read")?,
Action::Write => out.write_all(b"write")?,
Action::Delete => out.write_all(b"delete")?,
Action::List => out.write_all(b"list")?,
Action::Admin => out.write_all(b"admin")?,
}
Ok(IsNull::No)
}
}
// Implement FromSql for the custom enum
impl FromSql<crate::postgres::schema::sql_types::Action, Pg> for Action {
fn from_sql(bytes: PgValue) -> deserialize::Result<Self> {
match bytes.as_bytes() {
b"read" => Ok(Action::Read),
b"write" => Ok(Action::Write),
b"delete" => Ok(Action::Delete),
b"list" => Ok(Action::List),
b"admin" => Ok(Action::Admin),
_ => Err("Unrecognized enum variant".into()),
}
}
}
#[derive(
Debug, Serialize, Deserialize, Identifiable, Associations, Queryable, Selectable, PartialEq,
)]
#[diesel(belongs_to(Credentials))]
#[diesel(table_name = directory_actions)]
#[diesel(check_for_backend(diesel::pg::Pg))]
pub struct DirectoryActions {
pub id: i32,
pub credentials_id: i32,
pub directory_path: String,
pub actions: Vec<Option<Action>>,
}
Sql code:
CREATE TYPE Action AS ENUM ('read', 'write', 'delete', 'list', 'admin');
CREATE TABLE
directory_actions (
id SERIAL PRIMARY KEY,
credentials_id INTEGER REFERENCES credentials(id) NOT NULL,
directory_path VARCHAR(255) NOT NULL,
actions Action[] NOT NULL check (actions <> '{}' and array_position(actions, null) is null)
);
Diesel schema:
pub mod sql_types {
#[derive(diesel::sql_types::SqlType)]
#[diesel(postgres_type(name = "action"))]
pub struct Action;
}
diesel::table! {
use diesel::sql_types::*;
use super::sql_types::Action;
directory_actions (id) {
id -> Int4,
credentials_id -> Int4,
#[max_length = 255]
directory_path -> Varchar,
actions -> Array<Nullable<Action>>,
}
}
Insertion code:
diesel::insert_into(directory_actions_schema::table)
.values(&directory_actions)
.execute(connection)
.await
.map_err(|e| {
log::error!("Error saving credentials to db: {}", e);
Error::FailedToCreate
})?;
What is the expected output?
Not an error
What is the actual output?
Failed to find a type oid for
action
Are you seeing any additional errors?
No
Steps to reproduce
Checklist
- I have already looked over the issue tracker and the discussion forum for similar possible closed issues.
- This issue can be reproduced on Rust's stable channel. (Your issue will be
closed if this is not the case) - This issue can be reproduced without requiring a third party crate
Thanks for opening this bug report.
I've tried the following code:
use std::io::Write;
use diesel::connection::SimpleConnection;
use diesel::deserialize;
use diesel::deserialize::FromSql;
use diesel::deserialize::FromSqlRow;
use diesel::expression::AsExpression;
use diesel::pg::Pg;
use diesel::pg::PgValue;
use diesel::prelude::*;
use diesel::serialize;
use diesel::serialize::IsNull;
use diesel::serialize::Output;
use diesel::serialize::ToSql;
pub mod postgres {
pub mod schema {
pub mod sql_types {
#[derive(diesel::sql_types::SqlType)]
#[diesel(postgres_type(name = "action"))]
pub struct Action;
}
diesel::table! {
use diesel::sql_types::*;
use super::sql_types::Action;
directory_actions (id) {
id -> Int4,
credentials_id -> Int4,
#[max_length = 255]
directory_path -> Varchar,
actions -> Array<Nullable<Action>>,
}
}
}
}
#[derive(Debug, FromSqlRow, AsExpression, Clone)]
#[diesel(sql_type = crate::postgres::schema::sql_types::Action)]
pub enum Action {
Read,
Write,
Delete,
List,
Admin,
}
// Implement ToSql for the custom enum
impl ToSql<crate::postgres::schema::sql_types::Action, Pg> for Action {
fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
match *self {
Action::Read => out.write_all(b"read")?,
Action::Write => out.write_all(b"write")?,
Action::Delete => out.write_all(b"delete")?,
Action::List => out.write_all(b"list")?,
Action::Admin => out.write_all(b"admin")?,
}
Ok(IsNull::No)
}
}
// Implement FromSql for the custom enum
impl FromSql<crate::postgres::schema::sql_types::Action, Pg> for Action {
fn from_sql(bytes: PgValue) -> deserialize::Result<Self> {
match bytes.as_bytes() {
b"read" => Ok(Action::Read),
b"write" => Ok(Action::Write),
b"delete" => Ok(Action::Delete),
b"list" => Ok(Action::List),
b"admin" => Ok(Action::Admin),
_ => Err("Unrecognized enum variant".into()),
}
}
}
#[derive(Debug, Identifiable, Queryable, Selectable, Insertable)]
#[diesel(table_name = crate::postgres::schema::directory_actions)]
#[diesel(check_for_backend(diesel::pg::Pg))]
pub struct DirectoryActions {
pub id: i32,
pub credentials_id: i32,
pub directory_path: String,
pub actions: Vec<Option<Action>>,
}
fn main() {
let mut conn = PgConnection::establish("postgres://localhost/diesel_test").unwrap();
conn.begin_test_transaction().unwrap();
conn.batch_execute("CREATE TYPE Action AS ENUM ('read', 'write', 'delete', 'list', 'admin');
CREATE TABLE
directory_actions (
id SERIAL PRIMARY KEY,
credentials_id INTEGER NOT NULL,
directory_path VARCHAR(255) NOT NULL,
actions Action[] NOT NULL check (actions <> '{}' and array_position(actions, null) is null)
);").unwrap();
let directory_actions = vec![DirectoryActions {
id: 1,
credentials_id: 1,
directory_path: "".into(),
actions: vec![Some(Action::Read)],
}];
diesel::insert_into(crate::postgres::schema::directory_actions::table)
.values(&directory_actions)
.execute(&mut conn)
.unwrap();
let res = crate::postgres::schema::directory_actions::table
.select(DirectoryActions::as_select())
.load(&mut conn)
.unwrap();
dbg!(res);
}
This runs without issues for me. I'm closing this issue as cannot reproduce. If you still hit this bug, feel free to provide a minimal self contained reproducible example (something someone can just do git clone … && cargo run
).
Thanks for you swift reply, your example indeed seems to work. So something is wrong in my project and I will try to figure out what. The one thing that is different is that we are using diesel-async
, could that be the problem?
The one thing that is different is that we are using diesel-async, could that be the problem?
If that's the problem you are in the wrong issue tracker here, as diesel-async
is a different project. If you open an issue there please try to remember providing a complete example.