systemprompt_database/services/postgres/
transaction.rs1use crate::error::{DatabaseResult, RepositoryError};
10use async_trait::async_trait;
11
12use super::conversion::{bind_params, row_to_json};
13use crate::models::{DatabaseTransaction, JsonRow, QuerySelector, ToDbValue};
14
15pub struct PostgresTransaction {
16 tx: Option<sqlx::Transaction<'static, sqlx::Postgres>>,
17}
18
19impl std::fmt::Debug for PostgresTransaction {
20 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21 f.debug_struct("PostgresTransaction")
22 .field("tx", &self.tx.is_some())
23 .finish()
24 }
25}
26
27impl PostgresTransaction {
28 #[must_use]
29 pub const fn new(tx: sqlx::Transaction<'static, sqlx::Postgres>) -> Self {
30 Self { tx: Some(tx) }
31 }
32}
33
34#[async_trait]
35impl DatabaseTransaction for PostgresTransaction {
36 async fn execute(
37 &mut self,
38 query: &dyn QuerySelector,
39 params: &[&dyn ToDbValue],
40 ) -> DatabaseResult<u64> {
41 let sql = query.select_query();
42 let tx = self
43 .tx
44 .as_mut()
45 .ok_or_else(|| RepositoryError::invalid_state("Transaction already consumed"))?;
46
47 let query_obj = sqlx::query(sqlx::AssertSqlSafe(sql));
48 let query_obj = bind_params(query_obj, params);
49
50 let result = query_obj.execute(&mut **tx).await?;
51
52 Ok(result.rows_affected())
53 }
54
55 async fn fetch_all(
56 &mut self,
57 query: &dyn QuerySelector,
58 params: &[&dyn ToDbValue],
59 ) -> DatabaseResult<Vec<JsonRow>> {
60 let sql = query.select_query();
61 let tx = self
62 .tx
63 .as_mut()
64 .ok_or_else(|| RepositoryError::invalid_state("Transaction already consumed"))?;
65
66 let query_obj = sqlx::query(sqlx::AssertSqlSafe(sql));
67 let query_obj = bind_params(query_obj, params);
68
69 let rows = query_obj.fetch_all(&mut **tx).await?;
70
71 Ok(rows.iter().map(row_to_json).collect())
72 }
73
74 async fn fetch_one(
75 &mut self,
76 query: &dyn QuerySelector,
77 params: &[&dyn ToDbValue],
78 ) -> DatabaseResult<JsonRow> {
79 let sql = query.select_query();
80 let tx = self
81 .tx
82 .as_mut()
83 .ok_or_else(|| RepositoryError::invalid_state("Transaction already consumed"))?;
84
85 let query_obj = sqlx::query(sqlx::AssertSqlSafe(sql));
86 let query_obj = bind_params(query_obj, params);
87
88 let row = query_obj.fetch_one(&mut **tx).await?;
89
90 Ok(row_to_json(&row))
91 }
92
93 async fn fetch_optional(
94 &mut self,
95 query: &dyn QuerySelector,
96 params: &[&dyn ToDbValue],
97 ) -> DatabaseResult<Option<JsonRow>> {
98 let sql = query.select_query();
99 let tx = self
100 .tx
101 .as_mut()
102 .ok_or_else(|| RepositoryError::invalid_state("Transaction already consumed"))?;
103
104 let query_obj = sqlx::query(sqlx::AssertSqlSafe(sql));
105 let query_obj = bind_params(query_obj, params);
106
107 let row = query_obj.fetch_optional(&mut **tx).await?;
108
109 Ok(row.map(|r| row_to_json(&r)))
110 }
111
112 async fn commit(mut self: Box<Self>) -> DatabaseResult<()> {
113 let tx = self
114 .tx
115 .take()
116 .ok_or_else(|| RepositoryError::invalid_state("Transaction already consumed"))?;
117
118 tx.commit().await?;
119
120 Ok(())
121 }
122
123 async fn rollback(mut self: Box<Self>) -> DatabaseResult<()> {
124 let tx = self
125 .tx
126 .take()
127 .ok_or_else(|| RepositoryError::invalid_state("Transaction already consumed"))?;
128
129 tx.rollback().await?;
130
131 Ok(())
132 }
133}