Use sql_function in a changeset

Hi!

I’m trying to update a uuid in an object by using a sql_function!(fn uuid_generate_v4() -> Uuid);
Is there a way to combine that with a changeset?

#[derive(AsChangeset, Debug, Deserialize, Serialize)]
#[table_name="users"]
pub struct UserUpdate {
  name: Option<String>,
  token: Option<Uuid>,
}

Thank you in advance :wink:

That is already possible with the released diesel 1.4.x version.

sql_function!(fn uuid_generate_v4() -> Uuid);

fn update_user(update: UserUpdate, connection: &PgConnection) -> Result<()> {
    diesel::update(update)
        .set((update, users::whatever.eq(uuid_generate_v4())))
        .execute(connection)?;
    Ok(())
}

But how can I make that optional?

And apparently, sql_function!(fn uuid_generate_v4() -> Uuid); doesn’t seem to work :thinking:

error: this must repeat at least once
  --> $HOME/.cargo/registry/src/github.com-1ecc6299db9ec823/diesel-1.4.4/src/expression/functions/mod.rs:33:20
   |
33 |             where $($arg_name: $crate::expression::AsExpression<$arg_type>),+
   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0425]: cannot find function `uuid_generate_v4` in this scope
  --> src/model/user.rs:99:38
   |
99 |       .set(users::dsl::token.eq(uuid_generate_v4()));
   |                                 ^^^^^^^^^^^^^^^^ not found in this scope

But how can I make that optional?

What do you mean by that? Using the provided uuid or generate a new one depending on the value of update.token? In that case I would just branch depending on that value.

And apparently, sql_function!(fn uuid_generate_v4() → Uuid); doesn’t seem to work

Right, you need to use no_arg_sql_function! for this on the latest release.

Well my goal is to update a value only if the field is_some().

My object is the following

#[derive(AsChangeset, Debug, Deserialize, Serialize)]
#[table_name="users"]
pub struct UserUpdate {
  name: Option<String>,
  token: Option<bool>,
}

And I want to update the name only when the name exists and only refresh the token when the token is set to true.
Do you have any solution in mind for that ?

I’ve tried to chain the .set depending on .is_some() but this is not really straightforward.

Something like this should work:

fn update_user(update: UserUpdate, connection: &PgConnection) -> Result<()> {
    // this will fail if both, name and token are none, so check that case if you don't want to error
    diesel::update(users::table)
        .set((
            update.name.map(|n| users::name.eq(n)), 
            update.token.map(|_| users::token.eq(uuid_generate_v4))
         )).execute(connection)?;
    Ok(())
}

Alternatively you can implement AsChangeset manually to return exactly those values passed to set