mod common;
use qbrs::insert::partial_index;
use qbrs::prelude::*;
use qbrs_sqlx::prelude::*;
#[derive(Table)]
#[table(name = "memberships")]
#[allow(dead_code)]
struct Memberships {
#[column(primary_key, generated)]
id: i64,
team: String,
handle: String,
left_at: Option<i64>,
}
#[tokio::test]
async fn a_conflict_target_reaches_a_partial_unique_index() {
let (pool, guard) = common::test_pool("qbrs_partial_index").await;
sqlx::query("DROP TABLE IF EXISTS memberships")
.execute(&pool)
.await
.expect("drop memberships");
sqlx::query(
"CREATE TABLE memberships (
id BIGSERIAL PRIMARY KEY,
team TEXT NOT NULL,
handle TEXT NOT NULL,
left_at BIGINT
)",
)
.execute(&pool)
.await
.expect("create memberships");
sqlx::query(
"CREATE UNIQUE INDEX memberships_current
ON memberships (team, handle) WHERE left_at IS NULL",
)
.execute(&pool)
.await
.expect("create the partial unique index");
let upsert = |handle: &'static str| {
qbrs::insert::insert(memberships::Table)
.values(
MembershipsInsert::builder()
.team("core")
.handle(handle)
.build(),
)
.on_conflict_do_update(
partial_index(
(memberships::team, memberships::handle),
memberships::left_at.is_null(),
),
qbrs::update::Assignments::from_row(MembershipsUpdate {
left_at: Some(Some(1)),
..Default::default()
})
.expect("left_at is set"),
)
};
assert_eq!(upsert("ada").execute(&pool).await.expect("first insert"), 1);
assert_eq!(upsert("ada").execute(&pool).await.expect("the upsert"), 1);
let rows: Vec<(String, Option<i64>)> = select((memberships::handle, memberships::left_at))
.from(memberships::Table)
.load(&pool)
.await
.expect("read the rows back")
.into_tuples();
assert_eq!(rows, vec![("ada".to_string(), Some(1))]);
assert_eq!(
upsert("ada").execute(&pool).await.expect("reuse the key"),
1
);
let current: i64 = select(qbrs::expr::count())
.from(memberships::Table)
.load_one(&pool)
.await
.expect("count the rows")
.expect("one row");
assert_eq!(current, 2);
let narrower = qbrs::insert::insert(memberships::Table)
.values(
MembershipsInsert::builder()
.team("core")
.handle("ada")
.build(),
)
.on_conflict_do_nothing(partial_index(
(memberships::team, memberships::handle),
memberships::left_at
.is_null()
.and(memberships::team.eq("core")),
))
.returning(memberships::id)
.load(&pool)
.await
.expect("a predicate that implies the index's own");
assert!(narrower.is_empty(), "the current row is the conflict");
let unmatched = qbrs::insert::insert(memberships::Table)
.values(
MembershipsInsert::builder()
.team("core")
.handle("ada")
.build(),
)
.on_conflict_do_nothing((memberships::team, memberships::handle))
.execute(&pool)
.await;
assert!(
unmatched.is_err(),
"bare columns must not reach the partial index"
);
common::shutdown(pool, guard).await;
}