use async_trait::async_trait;
use sqlx::{Pool, Sqlite};
use crate::{
error::DatabaseError,
model::zone_snapshot::ZoneSnapshot,
repository::{RepositoryTx, RepositoryTxKind, ZoneSnapshotRepository},
};
pub struct SqliteZoneSnapshotRepository {
pool: Pool<Sqlite>,
}
impl SqliteZoneSnapshotRepository {
pub fn new(pool: Pool<Sqlite>) -> Self {
Self { pool }
}
}
#[async_trait]
impl ZoneSnapshotRepository for SqliteZoneSnapshotRepository {
async fn upsert(&self, snapshot: ZoneSnapshot) -> Result<ZoneSnapshot, DatabaseError> {
sqlx::query(
r#"
INSERT INTO zone_soa_history (zone_id, serial, primary_ns, admin_email, ttl, refresh, retry, expire, minimum_ttl)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(zone_id, serial)
DO UPDATE SET
primary_ns = excluded.primary_ns,
admin_email = excluded.admin_email,
ttl = excluded.ttl,
refresh = excluded.refresh,
retry = excluded.retry,
expire = excluded.expire,
minimum_ttl = excluded.minimum_ttl
"#,
)
.bind(snapshot.zone_id)
.bind(snapshot.serial)
.bind(&snapshot.primary_ns)
.bind(&snapshot.admin_email)
.bind(snapshot.ttl)
.bind(snapshot.refresh)
.bind(snapshot.retry)
.bind(snapshot.expire)
.bind(snapshot.minimum_ttl)
.execute(&self.pool)
.await
.map_err(|e| DatabaseError::QueryFailed(e.to_string()))?;
sqlx::query_as::<_, ZoneSnapshot>(
r#"
SELECT id, zone_id, serial, primary_ns, admin_email, ttl, refresh, retry, expire, minimum_ttl, created_at
FROM zone_soa_history
WHERE zone_id = ? AND serial = ?
"#,
)
.bind(snapshot.zone_id)
.bind(snapshot.serial)
.fetch_one(&self.pool)
.await
.map_err(|e| DatabaseError::QueryFailed(e.to_string()))
}
async fn upsert_tx(
&self,
tx: &mut RepositoryTx<'_>,
snapshot: ZoneSnapshot,
) -> Result<ZoneSnapshot, DatabaseError> {
let sqlite_tx = match &mut tx.0 {
RepositoryTxKind::SQLite(tx) => tx,
_ => {
return Err(DatabaseError::TransactionFailed(
"transaction kind mismatch (expected SQLite)".to_string(),
));
}
};
sqlx::query(
r#"
INSERT INTO zone_soa_history (zone_id, serial, primary_ns, admin_email, ttl, refresh, retry, expire, minimum_ttl)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(zone_id, serial)
DO UPDATE SET
primary_ns = excluded.primary_ns,
admin_email = excluded.admin_email,
ttl = excluded.ttl,
refresh = excluded.refresh,
retry = excluded.retry,
expire = excluded.expire,
minimum_ttl = excluded.minimum_ttl
"#,
)
.bind(snapshot.zone_id)
.bind(snapshot.serial)
.bind(&snapshot.primary_ns)
.bind(&snapshot.admin_email)
.bind(snapshot.ttl)
.bind(snapshot.refresh)
.bind(snapshot.retry)
.bind(snapshot.expire)
.bind(snapshot.minimum_ttl)
.execute(&mut **sqlite_tx)
.await
.map_err(|e| DatabaseError::QueryFailed(e.to_string()))?;
sqlx::query_as::<_, ZoneSnapshot>(
r#"
SELECT id, zone_id, serial, primary_ns, admin_email, ttl, refresh, retry, expire, minimum_ttl, created_at
FROM zone_soa_history
WHERE zone_id = ? AND serial = ?
"#,
)
.bind(snapshot.zone_id)
.bind(snapshot.serial)
.fetch_one(&mut **sqlite_tx)
.await
.map_err(|e| DatabaseError::QueryFailed(e.to_string()))
}
async fn get_by_zone_id_and_serial(
&self,
zone_id: i32,
serial: i32,
) -> Result<Option<ZoneSnapshot>, DatabaseError> {
sqlx::query_as::<_, ZoneSnapshot>(
r#"
SELECT id, zone_id, serial, primary_ns, admin_email, ttl, refresh, retry, expire, minimum_ttl, created_at
FROM zone_soa_history
WHERE zone_id = ? AND serial = ?
"#,
)
.bind(zone_id)
.bind(serial)
.fetch_optional(&self.pool)
.await
.map_err(|e| DatabaseError::QueryFailed(e.to_string()))
}
}