dbkit-core 0.3.1

Core types and SQL building blocks for the dbkit ORM.
Documentation
use chrono::NaiveDateTime;
use dbkit_core::{func, interval, Column, Order, Select, Table};

#[derive(Debug)]
struct WorkRun;

fn work_runs_table() -> Table {
    Table::new("work_runs")
}

fn work_run_status() -> Column<WorkRun, String> {
    Column::new(work_runs_table(), "status")
}

fn work_run_attempts() -> Column<WorkRun, i32> {
    Column::new(work_runs_table(), "attempts")
}

fn work_run_updated_at() -> Column<WorkRun, NaiveDateTime> {
    Column::new(work_runs_table(), "updated_at")
}

fn work_run_created_at() -> Column<WorkRun, NaiveDateTime> {
    Column::new(work_runs_table(), "created_at")
}

#[test]
fn compiles_query_with_dynamic_interval_math() {
    let now = chrono::DateTime::from_timestamp(1_700_000_000, 0).expect("now").naive_utc();
    let stale_timeout_seconds = 300_i64;
    let retry_base_seconds = 60.0_f64;
    let retry_cap_seconds = 3_600.0_f64;

    let retry_exponent = func::least(func::greatest(work_run_attempts() - 1_i32, 0_i32), 10_i32);
    let retry_seconds = func::least(retry_cap_seconds, func::power(2.0_f64, retry_exponent) * retry_base_seconds);

    let sql = Select::<WorkRun>::new(work_runs_table())
        .filter(
            work_run_status()
                .eq("pending")
                .or(work_run_status()
                    .eq("running")
                    .and(work_run_updated_at().le(now - interval::seconds(stale_timeout_seconds))))
                .or(work_run_status()
                    .eq("failed")
                    .and(work_run_updated_at().le(now - interval::seconds(retry_seconds)))),
        )
        .order_by(Order::asc(work_run_attempts()))
        .order_by(Order::asc(work_run_created_at()))
        .for_update()
        .skip_locked()
        .limit(1)
        .compile();

    assert!(sql.sql.contains("FROM work_runs"), "unexpected SQL: {}", sql.sql);
    assert!(sql.sql.contains("MAKE_INTERVAL(secs => $"), "unexpected SQL: {}", sql.sql);
    assert!(sql.sql.contains("MAKE_INTERVAL(secs => LEAST("), "unexpected SQL: {}", sql.sql);
    assert!(sql.sql.contains("POWER($"), "unexpected SQL: {}", sql.sql);
    assert!(sql.sql.contains("GREATEST((work_runs.attempts - $"), "unexpected SQL: {}", sql.sql);
    assert!(
        sql.sql.contains("ORDER BY work_runs.attempts ASC, work_runs.created_at ASC"),
        "unexpected SQL: {}",
        sql.sql
    );
    assert!(sql.sql.ends_with("FOR UPDATE SKIP LOCKED"), "unexpected SQL: {}", sql.sql);
}