bindizr_db/repository/postgres/
zone_snapshot_repository_impl.rs1use 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}