use crate::error::Result;
use super::{StateConn, StateStore};
impl StateStore {
pub fn mark_snapshot_done(
&self,
export_name: &str,
table_name: &str,
run_id: &str,
) -> Result<()> {
let now = chrono::Utc::now().to_rfc3339();
match &self.conn {
StateConn::Sqlite(c) => {
c.execute(
"INSERT OR REPLACE INTO cdc_snapshot
(export_name, table_name, run_id, completed_at)
VALUES (?1, ?2, ?3, ?4)",
rusqlite::params![export_name, table_name, run_id, now],
)?;
}
StateConn::Postgres(client) => {
let mut c = client.borrow_mut();
c.execute(
"INSERT INTO cdc_snapshot
(export_name, table_name, run_id, completed_at)
VALUES ($1, $2, $3, $4)
ON CONFLICT (export_name, table_name) DO UPDATE SET
run_id = excluded.run_id,
completed_at = excluded.completed_at",
&[&export_name, &table_name, &run_id, &now],
)?;
}
}
Ok(())
}
pub fn snapshot_done(&self, export_name: &str, table_name: &str) -> Result<bool> {
match &self.conn {
StateConn::Sqlite(c) => {
let n: i64 = c.query_row(
"SELECT COUNT(*) FROM cdc_snapshot
WHERE export_name = ?1 AND table_name = ?2",
rusqlite::params![export_name, table_name],
|r| r.get(0),
)?;
Ok(n > 0)
}
StateConn::Postgres(client) => {
let mut c = client.borrow_mut();
let row = c.query_one(
"SELECT COUNT(*) FROM cdc_snapshot
WHERE export_name = $1 AND table_name = $2",
&[&export_name, &table_name],
)?;
Ok(row.get::<_, i64>(0) > 0)
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn snapshot_done_is_false_until_marked_then_true() {
let s = StateStore::open_in_memory().unwrap();
assert!(!s.snapshot_done("customers", "customers").unwrap());
s.mark_snapshot_done("customers", "customers", "run_1")
.unwrap();
assert!(s.snapshot_done("customers", "customers").unwrap());
assert!(!s.snapshot_done("customers", "orders").unwrap());
}
#[test]
fn mark_snapshot_done_is_idempotent() {
let s = StateStore::open_in_memory().unwrap();
s.mark_snapshot_done("e", "t", "run_1").unwrap();
s.mark_snapshot_done("e", "t", "run_2").unwrap(); assert!(s.snapshot_done("e", "t").unwrap());
}
}