Skip to main content

bindizr_db/repository/sqlite/
zone_snapshot_repository_impl.rs

1use async_trait::async_trait;
2use sqlx::{Pool, Sqlite};
3
4use crate::{
5    error::DatabaseError,
6    model::zone_snapshot::ZoneSnapshot,
7    repository::{RepositoryTx, RepositoryTxKind, ZoneSnapshotRepository},
8};
9
10pub struct SqliteZoneSnapshotRepository {
11    pool: Pool<Sqlite>,
12}
13
14impl SqliteZoneSnapshotRepository {
15    pub fn new(pool: Pool<Sqlite>) -> Self {
16        Self { pool }
17    }
18}
19
20#[async_trait]
21impl ZoneSnapshotRepository for SqliteZoneSnapshotRepository {
22    async fn upsert(&self, snapshot: ZoneSnapshot) -> Result<ZoneSnapshot, DatabaseError> {
23        sqlx::query(
24            r#"
25            INSERT INTO zone_soa_history (zone_id, serial, primary_ns, admin_email, ttl, refresh, retry, expire, minimum_ttl)
26            VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
27            ON CONFLICT(zone_id, serial)
28            DO UPDATE SET
29                primary_ns = excluded.primary_ns,
30                admin_email = excluded.admin_email,
31                ttl = excluded.ttl,
32                refresh = excluded.refresh,
33                retry = excluded.retry,
34                expire = excluded.expire,
35                minimum_ttl = excluded.minimum_ttl
36            "#,
37        )
38        .bind(snapshot.zone_id)
39        .bind(snapshot.serial)
40        .bind(&snapshot.primary_ns)
41        .bind(&snapshot.admin_email)
42        .bind(snapshot.ttl)
43        .bind(snapshot.refresh)
44        .bind(snapshot.retry)
45        .bind(snapshot.expire)
46        .bind(snapshot.minimum_ttl)
47        .execute(&self.pool)
48        .await
49        .map_err(|e| DatabaseError::QueryFailed(e.to_string()))?;
50
51        sqlx::query_as::<_, ZoneSnapshot>(
52            r#"
53            SELECT id, zone_id, serial, primary_ns, admin_email, ttl, refresh, retry, expire, minimum_ttl, created_at
54            FROM zone_soa_history
55            WHERE zone_id = ? AND serial = ?
56            "#,
57        )
58        .bind(snapshot.zone_id)
59        .bind(snapshot.serial)
60        .fetch_one(&self.pool)
61        .await
62        .map_err(|e| DatabaseError::QueryFailed(e.to_string()))
63    }
64
65    async fn upsert_tx(
66        &self,
67        tx: &mut RepositoryTx<'_>,
68        snapshot: ZoneSnapshot,
69    ) -> Result<ZoneSnapshot, DatabaseError> {
70        let sqlite_tx = match &mut tx.0 {
71            RepositoryTxKind::SQLite(tx) => tx,
72            _ => {
73                return Err(DatabaseError::TransactionFailed(
74                    "transaction kind mismatch (expected SQLite)".to_string(),
75                ));
76            }
77        };
78
79        sqlx::query(
80            r#"
81            INSERT INTO zone_soa_history (zone_id, serial, primary_ns, admin_email, ttl, refresh, retry, expire, minimum_ttl)
82            VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
83            ON CONFLICT(zone_id, serial)
84            DO UPDATE SET
85                primary_ns = excluded.primary_ns,
86                admin_email = excluded.admin_email,
87                ttl = excluded.ttl,
88                refresh = excluded.refresh,
89                retry = excluded.retry,
90                expire = excluded.expire,
91                minimum_ttl = excluded.minimum_ttl
92            "#,
93        )
94        .bind(snapshot.zone_id)
95        .bind(snapshot.serial)
96        .bind(&snapshot.primary_ns)
97        .bind(&snapshot.admin_email)
98        .bind(snapshot.ttl)
99        .bind(snapshot.refresh)
100        .bind(snapshot.retry)
101        .bind(snapshot.expire)
102        .bind(snapshot.minimum_ttl)
103        .execute(&mut **sqlite_tx)
104        .await
105        .map_err(|e| DatabaseError::QueryFailed(e.to_string()))?;
106
107        sqlx::query_as::<_, ZoneSnapshot>(
108            r#"
109            SELECT id, zone_id, serial, primary_ns, admin_email, ttl, refresh, retry, expire, minimum_ttl, created_at
110            FROM zone_soa_history
111            WHERE zone_id = ? AND serial = ?
112            "#,
113        )
114        .bind(snapshot.zone_id)
115        .bind(snapshot.serial)
116        .fetch_one(&mut **sqlite_tx)
117        .await
118        .map_err(|e| DatabaseError::QueryFailed(e.to_string()))
119    }
120
121    async fn get_by_zone_id_and_serial(
122        &self,
123        zone_id: i32,
124        serial: i32,
125    ) -> Result<Option<ZoneSnapshot>, DatabaseError> {
126        sqlx::query_as::<_, ZoneSnapshot>(
127            r#"
128            SELECT id, zone_id, serial, primary_ns, admin_email, ttl, refresh, retry, expire, minimum_ttl, created_at
129            FROM zone_soa_history
130            WHERE zone_id = ? AND serial = ?
131            "#,
132        )
133        .bind(zone_id)
134        .bind(serial)
135        .fetch_optional(&self.pool)
136        .await
137        .map_err(|e| DatabaseError::QueryFailed(e.to_string()))
138    }
139}