dbkit-core 0.2.1

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

#[derive(Debug)]
struct Schedule;

fn schedule_table() -> Table {
    Table::new("schedules")
}

fn schedule_base_interval_hours() -> Column<Schedule, i32> {
    Column::new(schedule_table(), "base_interval_hours")
}

fn schedule_backoff_minutes() -> Column<Schedule, i32> {
    Column::new(schedule_table(), "backoff_minutes")
}

fn schedule_retry_interval() -> Column<Schedule, dbkit_core::PgInterval> {
    Column::new(schedule_table(), "retry_interval")
}

#[test]
fn compiles_interval_hours_with_literal() {
    let query: Select<Schedule> = Select::new(schedule_table())
        .select_only()
        .column_as(dbkit_core::interval::hours(6_i32), "lease_window");

    let sql = query.compile();
    assert_eq!(sql.sql, "SELECT MAKE_INTERVAL(hours => $1) AS lease_window FROM schedules");
    assert_eq!(sql.binds, vec![Value::I32(6)]);
}

#[test]
fn compiles_interval_hours_with_column() {
    let query: Select<Schedule> = Select::new(schedule_table())
        .select_only()
        .column_as(dbkit_core::interval::hours(schedule_base_interval_hours()), "lease_window");

    let sql = query.compile();
    assert_eq!(
        sql.sql,
        "SELECT MAKE_INTERVAL(hours => schedules.base_interval_hours) AS lease_window FROM schedules"
    );
    assert!(sql.binds.is_empty());
}

#[test]
fn compiles_interval_hours_with_nested_expression_part() {
    let query: Select<Schedule> = Select::new(schedule_table()).select_only().column_as(
        dbkit_core::interval::hours(func::coalesce(schedule_base_interval_hours(), 24_i32)),
        "lease_window",
    );

    let sql = query.compile();
    assert_eq!(
        sql.sql,
        "SELECT MAKE_INTERVAL(hours => COALESCE(schedules.base_interval_hours, $1)) AS lease_window FROM schedules"
    );
    assert_eq!(sql.binds, vec![Value::I32(24)]);
}

#[test]
fn compiles_interval_minutes_with_negative_literal() {
    let query: Select<Schedule> = Select::new(schedule_table())
        .select_only()
        .column_as(dbkit_core::interval::minutes(-30_i32), "retry_after");

    let sql = query.compile();
    assert_eq!(sql.sql, "SELECT MAKE_INTERVAL(mins => $1) AS retry_after FROM schedules");
    assert_eq!(sql.binds, vec![Value::I32(-30)]);
}

#[test]
fn compiles_interval_days_with_literal() {
    let query: Select<Schedule> = Select::new(schedule_table())
        .select_only()
        .column_as(dbkit_core::interval::days(2_i32), "cooldown");

    let sql = query.compile();
    assert_eq!(sql.sql, "SELECT MAKE_INTERVAL(days => $1) AS cooldown FROM schedules");
    assert_eq!(sql.binds, vec![Value::I32(2)]);
}

#[test]
fn compiles_interval_seconds_with_fractional_literal() {
    let query: Select<Schedule> = Select::new(schedule_table())
        .select_only()
        .column_as(dbkit_core::interval::seconds(1.5_f64), "jitter");

    let sql = query.compile();
    assert_eq!(sql.sql, "SELECT MAKE_INTERVAL(secs => $1) AS jitter FROM schedules");
    assert_eq!(sql.binds, vec![Value::F64(1.5)]);
}

#[test]
fn interval_expression_can_compare_to_interval_columns() {
    let query: Select<Schedule> =
        Select::new(schedule_table()).filter(dbkit_core::interval::hours(1_i32).eq_col(schedule_retry_interval()));

    let sql = query.compile();
    assert_eq!(
        sql.sql,
        "SELECT schedules.* FROM schedules WHERE (MAKE_INTERVAL(hours => $1) = schedules.retry_interval)"
    );
    assert_eq!(sql.binds, vec![Value::I32(1)]);
}

#[test]
fn interval_expression_can_be_used_in_order_by() {
    let query: Select<Schedule> = Select::new(schedule_table())
        .order_by(Order::asc(dbkit_core::interval::minutes(schedule_backoff_minutes())))
        .limit(10);

    let sql = query.compile();
    assert_eq!(
        sql.sql,
        "SELECT schedules.* FROM schedules ORDER BY MAKE_INTERVAL(mins => schedules.backoff_minutes) ASC LIMIT 10"
    );
    assert!(sql.binds.is_empty());
}