hiqlite 0.13.2

Hiqlite - highly-available, embeddable, raft-based SQLite + cache
Documentation
use crate::log;
use chrono::Utc;
use hiqlite::macros::params;
use hiqlite::{Client, Error};
use serde::{Deserialize, Serialize};
use std::time::Duration;
use tokio::time;

// serde derives are mandatory if we want to use the `query_as()`
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct TestData {
    pub id: i64,
    pub ts: i64,
    pub description: Option<String>,
}

impl From<&mut hiqlite::Row<'_>> for TestData {
    fn from(row: &mut hiqlite::Row<'_>) -> Self {
        Self {
            id: row.get("id"),
            ts: row.get("ts"),
            description: row.get("description"),
        }
    }
}

pub async fn test_execute_query(
    client_1: &Client,
    client_2: &Client,
    client_3: &Client,
) -> Result<(), Error> {
    log("Inserting test data");

    let data = TestData {
        id: 1,
        ts: Utc::now().timestamp(),
        description: Some("My First Row from client 1".to_string()),
    };
    let rows_affected = client_1
        .execute(
            "INSERT INTO test VALUES ($1, $2, $3)",
            params!(data.id, data.ts, data.description.clone()),
        )
        .await?;
    assert_eq!(rows_affected, 1);

    log("Making sure clients 2 and 3 can read the same data");
    time::sleep(Duration::from_millis(500)).await;

    let res: TestData = client_2
        .query_as_one("SELECT * FROM test WHERE id = $1", params!(1))
        .await?;
    assert_eq!(res.id, data.id);
    assert_eq!(res.ts, data.ts);
    assert_eq!(res.description, data.description);

    let res: TestData = client_3
        .query_map_one("SELECT * FROM test WHERE id = $1", params!(1))
        .await?;
    assert_eq!(res.id, data.id);
    assert_eq!(res.ts, data.ts);
    assert_eq!(res.description, data.description);

    log("Making sure the same insert and read works on the other nodes as well");

    let data = TestData {
        id: 2,
        ts: Utc::now().timestamp(),
        description: Some("My First Row from client 2".to_string()),
    };
    let rows_affected = client_2
        .execute(
            "INSERT INTO test VALUES ($1, $2, $3)",
            params!(data.id, data.ts, data.description.clone()),
        )
        .await?;
    assert_eq!(rows_affected, 1);

    log("Making sure clients 2 and 3 can read the same data");

    let res: TestData = client_1
        .query_as_one("SELECT * FROM test WHERE id = $1", params!(2))
        .await?;
    assert_eq!(res.id, data.id);
    assert_eq!(res.ts, data.ts);
    assert_eq!(res.description, data.description);

    let res: TestData = client_3
        .query_map_one("SELECT * FROM test WHERE id = $1", params!(2))
        .await?;
    assert_eq!(res.id, data.id);
    assert_eq!(res.ts, data.ts);
    assert_eq!(res.description, data.description);

    let data = TestData {
        id: 3,
        ts: Utc::now().timestamp(),
        description: None,
    };
    let rows_affected = client_3
        .execute(
            "INSERT INTO test VALUES ($1, $2, $3)",
            params!(data.id, data.ts, data.description.clone()),
        )
        .await?;
    assert_eq!(rows_affected, 1);

    log("Making sure clients 2 and 3 can read the same data");

    let res: TestData = client_1
        .query_as_one("SELECT * FROM test WHERE id = $1", params!(3))
        .await?;
    assert_eq!(res.id, data.id);
    assert_eq!(res.ts, data.ts);
    assert_eq!(res.description, data.description);

    let res: TestData = client_2
        .query_map_one("SELECT * FROM test WHERE id = $1", params!(3))
        .await?;
    assert_eq!(res.id, data.id);
    assert_eq!(res.ts, data.ts);
    assert_eq!(res.description, data.description);

    log("Expecting unique key constraint error from SQLite");
    let res = client_3
        .execute(
            "INSERT INTO test VALUES ($1, $2, $3)",
            params!(data.id, data.ts, data.description.clone()),
        )
        .await;
    assert!(res.is_err());
    let err = format!("{:?}", res);
    assert!(err.starts_with("Err(ConstraintViolation("));

    log("DELETE query and make sure data is gone");
    let rows_affected = client_1
        .execute("DELETE FROM test WHERE id = $1", params!(1))
        .await?;
    assert_eq!(rows_affected, 1);

    let res: Result<TestData, Error> = client_1
        .query_as_one("SELECT * FROM test WHERE id = $1", params!(1))
        .await;
    assert!(res.is_err());

    log("Query multiple rows with 'query_as()'");
    let res: Vec<TestData> = client_1.query_as("SELECT * FROM test", params!()).await?;
    assert_eq!(res.len(), 2);

    log("Query multiple rows with 'query_map()'");
    let res: Vec<TestData> = client_1.query_map("SELECT * FROM test", params!()).await?;
    assert_eq!(res.len(), 2);

    log("Query consistent from all clients");
    let res: Vec<TestData> = client_1
        .query_consistent_map("SELECT * FROM test WHERE id = $1", params!(3))
        .await?;
    assert_eq!(res[0].id, data.id);
    assert_eq!(res[0].ts, data.ts);
    assert_eq!(res[0].description, data.description);

    let res: Vec<TestData> = client_2
        .query_consistent_map("SELECT * FROM test WHERE id = $1", params!(3))
        .await?;
    assert_eq!(res[0].id, data.id);
    assert_eq!(res[0].ts, data.ts);
    assert_eq!(res[0].description, data.description);

    let res: Vec<TestData> = client_3
        .query_consistent_map("SELECT * FROM test WHERE id = $1", params!(3))
        .await?;
    assert_eq!(res[0].id, data.id);
    assert_eq!(res[0].ts, data.ts);
    assert_eq!(res[0].description, data.description);

    log("Test Execute Returning RAW");
    let data = TestData {
        id: 7,
        ts: Utc::now().timestamp(),
        description: Some("Execute Returning Data".to_string()),
    };
    let mut rows = client_1
        .execute_returning(
            "INSERT INTO test VALUES ($1, $2, $3) RETURNING *",
            params!(data.id, data.ts, data.description.clone()),
        )
        .await?;
    assert_eq!(rows.len(), 1);
    let mut row = rows.remove(0)?;
    assert_eq!(row.get::<i64>("id"), data.id);
    assert_eq!(row.get::<i64>("ts"), data.ts);
    assert_eq!(
        row.get::<Option<String>>("description").as_deref(),
        Some("Execute Returning Data")
    );

    log("Test Execute Returning Mapped");
    let data = TestData {
        id: 8,
        ts: Utc::now().timestamp(),
        description: None,
    };
    let mut rows: Vec<Result<TestData, Error>> = client_1
        .execute_returning_map(
            "INSERT INTO test VALUES ($1, $2, $3) RETURNING *",
            params!(data.id, data.ts, data.description.clone()),
        )
        .await?;
    assert_eq!(rows.len(), 1);
    let row = rows.remove(0)?;
    assert_eq!(row.id, data.id);
    assert_eq!(row.ts, data.ts);
    assert_eq!(row.description, data.description);

    Ok(())
}