chain-builder 2.1.0

A typed, dialect-aware SQL query builder for Rust (PostgreSQL/MySQL/SQLite).
Documentation
//! Row-locking clauses (`FOR UPDATE` / `FOR SHARE`, `SKIP LOCKED` / `NOWAIT`).

use chain_builder::{MySql, Postgres, QueryBuilder, Sqlite};

#[test]
fn for_update_pg() {
    let (sql, _) = QueryBuilder::<Postgres>::table("jobs")
        .select(["id"])
        .where_eq("status", "queued")
        .for_update()
        .to_sql();
    assert_eq!(
        sql,
        r#"SELECT "id" FROM "jobs" WHERE "status" = $1 FOR UPDATE"#
    );
}

#[test]
fn for_share_pg() {
    let (sql, _) = QueryBuilder::<Postgres>::table("jobs")
        .select(["id"])
        .for_share()
        .to_sql();
    assert_eq!(sql, r#"SELECT "id" FROM "jobs" FOR SHARE"#);
}

#[test]
fn for_update_skip_locked() {
    let (sql, _) = QueryBuilder::<Postgres>::table("jobs")
        .select(["id"])
        .for_update()
        .skip_locked()
        .to_sql();
    assert_eq!(sql, r#"SELECT "id" FROM "jobs" FOR UPDATE SKIP LOCKED"#);
}

#[test]
fn for_update_nowait() {
    let (sql, _) = QueryBuilder::<Postgres>::table("jobs")
        .select(["id"])
        .for_update()
        .no_wait()
        .to_sql();
    assert_eq!(sql, r#"SELECT "id" FROM "jobs" FOR UPDATE NOWAIT"#);
}

#[test]
fn skip_locked_defaults_to_for_update() {
    // Calling skip_locked() without a prior for_update()/for_share() defaults the
    // strength to FOR UPDATE (the job-queue idiom).
    let (sql, _) = QueryBuilder::<Postgres>::table("jobs")
        .select(["id"])
        .skip_locked()
        .to_sql();
    assert_eq!(sql, r#"SELECT "id" FROM "jobs" FOR UPDATE SKIP LOCKED"#);
}

#[test]
fn lock_after_limit() {
    let (sql, binds) = QueryBuilder::<Postgres>::table("jobs")
        .select(["id"])
        .limit(1)
        .for_update()
        .skip_locked()
        .to_sql();
    assert_eq!(
        sql,
        r#"SELECT "id" FROM "jobs" LIMIT $1 FOR UPDATE SKIP LOCKED"#
    );
    assert_eq!(binds.len(), 1);
}

#[test]
fn for_update_mysql() {
    let (sql, _) = QueryBuilder::<MySql>::table("jobs")
        .select(["id"])
        .for_update()
        .skip_locked()
        .to_sql();
    assert_eq!(sql, "SELECT `id` FROM `jobs` FOR UPDATE SKIP LOCKED");
}

#[test]
fn locking_is_noop_on_sqlite() {
    // SQLite locks the whole database, not rows: the clause is silently dropped.
    let (sql, _) = QueryBuilder::<Sqlite>::table("jobs")
        .select(["id"])
        .for_update()
        .skip_locked()
        .to_sql();
    assert_eq!(sql, r#"SELECT "id" FROM "jobs""#);
}

#[test]
fn for_share_then_skip_locked_preserves_strength() {
    let (sql, _) = QueryBuilder::<Postgres>::table("jobs")
        .select(["id"])
        .for_share()
        .skip_locked()
        .to_sql();
    assert_eq!(sql, r#"SELECT "id" FROM "jobs" FOR SHARE SKIP LOCKED"#);
}