Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Failed to save array of enums #122

Open
DylanVerstraete opened this issue Oct 25, 2023 · 1 comment
Open

Failed to save array of enums #122

DylanVerstraete opened this issue Oct 25, 2023 · 1 comment
Labels
bug Something isn't working

Comments

@DylanVerstraete
Copy link

DylanVerstraete commented Oct 25, 2023

Setup

Versions

  • rustc: 1.72.0
  • diesel: 2.1.3
  • diesel-async: 0.4.1
  • Postgres 15.4
  • MacOs

Feature Flags

  • diesel:: ["postgres", "chrono"]
  • diesel_async:: ["postgres", "deadpool", "async-connection-wrapper, "tokio"]

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.

Use following code:

use std::io::Write;

use anyhow::Result;
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;
use diesel_async::{AsyncConnection, AsyncPgConnection, RunQueryDsl, SimpleAsyncConnection};

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>>,
}

#[tokio::main]
async fn main() -> Result<()> {
    let mut conn =
        AsyncPgConnection::establish("postgres://localhost/diesel_test")
            .await
            .unwrap();

    conn.begin_test_transaction().await.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)
    );",
    )
    .await
    .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)
        .await
        .unwrap();

    let res = crate::postgres::schema::directory_actions::table
        .select(DirectoryActions::as_select())
        .load(&mut conn)
        .await
        .unwrap();
    dbg!(res);

    Ok(())
}

Cargo.toml:

[dependencies]
tokio = { version = "1.32.0", features = ["full"] }
diesel = { version = "2.1.3", features = ["postgres", "chrono"] }
diesel-async = { version = "0.4.1", features = [
    "postgres",
    "deadpool",
    "async-connection-wrapper",
    "tokio"
] }
anyhow = "1.0"

What are you trying to accomplish?

Persist a list of enums

What is the expected output?

No error

What is the actual output?

thread 'main' panicked at src/main.rs:122:10:
called Result::unwrap() on an Err value: SerializationError(FailedToLookupTypeError(PgMetadataCacheKey { schema: None, type_name: "action" }))
note: run with RUST_BACKTRACE=1 environment variable to display a backtrace

Are you seeing any additional errors?

No

@DylanVerstraete DylanVerstraete added the bug Something isn't working label Oct 25, 2023
@weiznich
Copy link
Owner

That's likely a duplicate of #103

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants