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

No way to express "excluded" in upsert where clause in SeaQL #842

Open
TapGhoul opened this issue Nov 24, 2024 · 1 comment
Open

No way to express "excluded" in upsert where clause in SeaQL #842

TapGhoul opened this issue Nov 24, 2024 · 1 comment

Comments

@TapGhoul
Copy link

TapGhoul commented Nov 24, 2024

Description

I want to express a specific update pattern in SeqQL, but there is no way to do so in the query builder.

As far as I can tell from parusing the source code, the only time the special excluded table can be generated as part of a query is in the assignment.

The equivalent query I want to build is as so:

INSERT INTO kv (key, value)
VALUES ('hi', 'blah')
ON CONFLICT (key) DO UPDATE SET value   = excluded.value,
                                version = version + 1
WHERE excluded.value <> value

however I am unable to generate the excluded.value method within the WHERE clause here. The closest I can get is

let query = Kv::insert(model)
    .on_conflict(
        OnConflict::column(kv::Column::Key)
            .update_column(kv::Column::Value)
            .value(kv::Column::Version, Expr::col(kv::Column::Version).add(1))
            .action_and_where(Expr::col(kv::Column::Value).not_equals(kv::Column::Value))
            .to_owned(),
    )
    .into_query()
    .to_string(SqliteQueryBuilder);

which evaluates to

INSERT INTO "kv" ("key", "value")
VALUES ('hi', 'blah')
ON CONFLICT ("key") DO UPDATE SET "value"   = "excluded"."value",
                                  "version" = "version" + 1
WHERE "value" <> "value"

Schema

SQL

CREATE TABLE IF NOT EXISTS "kv" ( "key" text NOT NULL PRIMARY KEY, "value" text NULL, "version" integer NOT NULL DEFAULT 0 );

Entity

//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.1

use sea_orm::entity::prelude::*;

#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[sea_orm(table_name = "kv")]
pub struct Model {
    #[sea_orm(primary_key, auto_increment = false, column_type = "Text")]
    pub key: String,
    #[sea_orm(column_type = "Text", nullable)]
    pub value: Option<String>,
    pub version: i32,
}

#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}

impl ActiveModelBehavior for ActiveModel {}

Versions

OS: Linux
Arch: AMD64
Database: SQLite,

libsqlite3-sys: 0.30.1
Bundled SQLite version: 3.46.0
Bundled SQLite source ID: 2024-05-23 13:25:27 96c92aba00c8375bc32fafcdf12429c58bd8aabfcadab6683e35bbb9cdebf19e

sea-orm: 1.1.1
sea-orm-macros: 1.1.1
sea-bae: 0.2.1
sea-query: 0.32.0
sea-orm-migration: 1.1.1
sea-orm-cli: 1.1.1
sea-schema: 0.16.0
sea-schema-derive: 0.3.0

@TapGhoul
Copy link
Author

Slight update: I got there in the end, but this really doesn't feel "ideal" - this was more me brute force trying things.

let query = Kv::insert(model)
    .on_conflict(
        OnConflict::column(kv::Column::Key)
            .update_column(kv::Column::Value)
            .value(kv::Column::Version, Expr::col(kv::Column::Version).add(1))
            .action_and_where(Expr::col(kv::Column::Value).ne(SimpleExpr::CustomWithExpr(
                r#""excluded".?"#.to_string(),
                vec![SimpleExpr::Column(kv::Column::Value.into_column_ref())],
            )))
            .to_owned(),
    )
    .into_query()
    .to_string(SqliteQueryBuilder);

It feels like this is an API hole.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant