Skip to main content

bindizr_db/repository/postgres/
zone_snapshot_repository_impl.rs

1use async_trait::async_trait;
2use sqlx::{Pool, Postgres};
3
4use crate::{
5    error::DatabaseError,
6    model::zone_snapshot::ZoneSnapshot,
7    repository::{RepositoryTx, RepositoryTxKind, ZoneSnapshotRepository},
8};
9
10pub struct PostgresZoneSnapshotRepository {
11    pool: Pool<Postgres>,
12}
13
14impl PostgresZoneSnapshotRepository {
15    pub fn new(pool: Pool<Postgres>) -> Self {
16        Self { pool }
17    }
18}
19
20#[async_trait]
21impl ZoneSnapshotRepository for PostgresZoneSnapshotRepository {
22    async fn upsert(&self, snapshot: ZoneSnapshot) -> Result<ZoneSnapshot, DatabaseError> {
23        sqlx::query_as::<_, ZoneSnapshot>(
24            r#"
25            INSERT INTO zone_soa_history (zone_id, serial, primary_ns, admin_email, ttl, refresh, retry, expire, minimum_ttl)
26            VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
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            RETURNING id, zone_id, serial, primary_ns, admin_email, ttl, refresh, retry, expire, minimum_ttl, created_at
37            "#,
38        )
39        .bind(snapshot.zone_id)
40        .bind(snapshot.serial)
41        .bind(&snapshot.primary_ns)
42        .bind(&snapshot.admin_email)
43        .bind(snapshot.ttl)
44        .bind(snapshot.refresh)
45        .bind(snapshot.retry)
46        .bind(snapshot.expire)
47        .bind(snapshot.minimum_ttl)
48        .fetch_one(&self.pool)
49        .await
50        .map_err(|e| DatabaseError::QueryFailed(e.to_string()))
51    }
52
53    async fn upsert_tx(
54        &self,
55        tx: &mut RepositoryTx<'_>,
56        snapshot: ZoneSnapshot,
57    ) -> Result<ZoneSnapshot, DatabaseError> {
58        let postgres_tx = match &mut tx.0 {
59            RepositoryTxKind::PostgreSQL(tx) => tx,
60            _ => {
61                return Err(DatabaseError::TransactionFailed(
62                    "transaction kind mismatch (expected PostgreSQL)".to_string(),
63                ));
64            }
65        };
66
67        sqlx::query_as::<_, ZoneSnapshot>(
68            r#"
69            INSERT INTO zone_soa_history (zone_id, serial, primary_ns, admin_email, ttl, refresh, retry, expire, minimum_ttl)
70            VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
71            ON CONFLICT (zone_id, serial)
72            DO UPDATE SET
73                primary_ns = EXCLUDED.primary_ns,
74                admin_email = EXCLUDED.admin_email,
75                ttl = EXCLUDED.ttl,
76                refresh = EXCLUDED.refresh,
77                retry = EXCLUDED.retry,
78                expire = EXCLUDED.expire,
79                minimum_ttl = EXCLUDED.minimum_ttl
80            RETURNING id, zone_id, serial, primary_ns, admin_email, ttl, refresh, retry, expire, minimum_ttl, created_at
81            "#,
82        )
83        .bind(snapshot.zone_id)
84        .bind(snapshot.serial)
85        .bind(&snapshot.primary_ns)
86        .bind(&snapshot.admin_email)
87        .bind(snapshot.ttl)
88        .bind(snapshot.refresh)
89        .bind(snapshot.retry)
90        .bind(snapshot.expire)
91        .bind(snapshot.minimum_ttl)
92        .fetch_one(&mut **postgres_tx)
93        .await
94        .map_err(|e| DatabaseError::QueryFailed(e.to_string()))
95    }
96
97    async fn get_by_zone_id_and_serial(
98        &self,
99        zone_id: i32,
100        serial: i32,
101    ) -> Result<Option<ZoneSnapshot>, DatabaseError> {
102        sqlx::query_as::<_, ZoneSnapshot>(
103            r#"
104            SELECT id, zone_id, serial, primary_ns, admin_email, ttl, refresh, retry, expire, minimum_ttl, created_at
105            FROM zone_soa_history
106            WHERE zone_id = $1 AND serial = $2
107            "#,
108        )
109        .bind(zone_id)
110        .bind(serial)
111        .fetch_optional(&self.pool)
112        .await
113        .map_err(|e| DatabaseError::QueryFailed(e.to_string()))
114    }
115}