1use async_trait::async_trait;
2use rusticx_core::{
3 adapter::DatabaseAdapter,
4 error::{Result, RusticxError},
5 model::TableSchema,
6 query::QueryBuilder,
7 value::{Row, Value},
8};
9use rusticx_sql::{compiler::SqlCompiler, dialect::PostgresDialect};
10use sqlx::{postgres::PgPoolOptions, PgPool, Row as SqlxRow};
11use tracing::debug;
12
13use crate::convert::pg_row_to_row;
14
15pub struct PostgresAdapter {
20 pool: PgPool,
21 dialect: PostgresDialect,
22}
23
24#[derive(Debug, Clone)]
34pub struct PostgresConfig {
35 pub url: String,
36 pub max_connections: u32,
37 pub min_connections: u32,
38}
39
40impl PostgresConfig {
41 pub fn new(url: impl Into<String>) -> Self {
42 Self {
43 url: url.into(),
44 max_connections: 10,
45 min_connections: 1,
46 }
47 }
48
49 pub fn max_connections(mut self, n: u32) -> Self {
50 self.max_connections = n;
51 self
52 }
53
54 pub fn min_connections(mut self, n: u32) -> Self {
55 self.min_connections = n;
56 self
57 }
58}
59
60impl PostgresAdapter {
61 pub async fn connect(config: PostgresConfig) -> Result<Self> {
62 let pool = PgPoolOptions::new()
63 .max_connections(config.max_connections)
64 .min_connections(config.min_connections)
65 .connect(&config.url)
66 .await
67 .map_err(|e| RusticxError::Connection(e.to_string()))?;
68
69 Ok(Self { pool, dialect: PostgresDialect })
70 }
71
72 pub async fn connect_url(url: impl Into<String>) -> Result<Self> {
73 Self::connect(PostgresConfig::new(url)).await
74 }
75
76 fn compiler(&self) -> SqlCompiler<'_, PostgresDialect> {
77 SqlCompiler::new(&self.dialect)
78 }
79
80 async fn execute_with_bindings(&self, sql: &str, bindings: Vec<Value>) -> Result<u64> {
82 let mut q = sqlx::query(sql);
83 for val in bindings {
84 q = bind_value(q, val);
85 }
86 let res = q.execute(&self.pool).await.map_err(|e| RusticxError::Query(e.to_string()))?;
87 Ok(res.rows_affected())
88 }
89
90 async fn fetch_rows_with_bindings(&self, sql: &str, bindings: Vec<Value>) -> Result<Vec<Row>> {
91 let mut q = sqlx::query(sql);
92 for val in bindings {
93 q = bind_value(q, val);
94 }
95 let rows = q.fetch_all(&self.pool).await.map_err(|e| RusticxError::Query(e.to_string()))?;
96 Ok(rows.into_iter().map(pg_row_to_row).collect())
97 }
98}
99
100fn bind_value<'q>(
101 q: sqlx::query::Query<'q, sqlx::Postgres, sqlx::postgres::PgArguments>,
102 val: Value,
103) -> sqlx::query::Query<'q, sqlx::Postgres, sqlx::postgres::PgArguments> {
104 match val {
105 Value::Null => q.bind(Option::<String>::None),
106 Value::Bool(b) => q.bind(b),
107 Value::Int(i) => q.bind(i),
108 Value::Float(f) => q.bind(f),
109 Value::Text(s) => q.bind(s),
110 Value::Bytes(b) => q.bind(b),
111 Value::Uuid(u) => q.bind(u),
112 Value::DateTime(dt) => q.bind(dt),
113 Value::Json(j) => q.bind(j),
114 Value::Array(arr) => {
115 let json = serde_json::to_value(&arr).unwrap_or(serde_json::Value::Null);
116 q.bind(json)
117 }
118 Value::Map(m) => {
119 let json = serde_json::to_value(&m).unwrap_or(serde_json::Value::Null);
120 q.bind(json)
121 }
122 }
123}
124
125#[async_trait]
126impl DatabaseAdapter for PostgresAdapter {
127 fn name(&self) -> &'static str {
128 "postgres"
129 }
130
131 async fn ping(&self) -> Result<()> {
132 sqlx::query("SELECT 1")
133 .execute(&self.pool)
134 .await
135 .map_err(|e| RusticxError::Connection(e.to_string()))?;
136 Ok(())
137 }
138
139 async fn close(&self) -> Result<()> {
140 self.pool.close().await;
141 Ok(())
142 }
143
144 async fn create_table(&self, schema: &TableSchema) -> Result<()> {
145 let sql = self.compiler().create_table(schema);
146 debug!(sql = %sql, "create_table");
147 for stmt in sql.split(';').map(str::trim).filter(|s| !s.is_empty()) {
149 sqlx::query(stmt)
150 .execute(&self.pool)
151 .await
152 .map_err(|e| RusticxError::Schema(e.to_string()))?;
153 }
154 Ok(())
155 }
156
157 async fn drop_table(&self, table: &str) -> Result<()> {
158 let sql = self.compiler().drop_table(table);
159 sqlx::query(&sql)
160 .execute(&self.pool)
161 .await
162 .map_err(|e| RusticxError::Schema(e.to_string()))?;
163 Ok(())
164 }
165
166 async fn table_exists(&self, table: &str) -> Result<bool> {
167 let row = sqlx::query(
168 "SELECT COUNT(*) as cnt FROM information_schema.tables WHERE table_name = $1",
169 )
170 .bind(table)
171 .fetch_one(&self.pool)
172 .await
173 .map_err(|e| RusticxError::Query(e.to_string()))?;
174 let cnt: i64 = row.try_get("cnt").unwrap_or(0);
175 Ok(cnt > 0)
176 }
177
178 async fn insert(&self, table: &str, row: Row) -> Result<Row> {
179 let pairs: Vec<(String, Value)> = row.into_iter().collect();
180 let (sql, bindings) = self.compiler().insert(table, &pairs);
181 debug!(sql = %sql, "insert");
182
183 let mut q = sqlx::query(&sql);
184 for val in bindings {
185 q = bind_value(q, val);
186 }
187
188 let pg_row = q
189 .fetch_one(&self.pool)
190 .await
191 .map_err(|e| RusticxError::Query(e.to_string()))?;
192
193 Ok(pg_row_to_row(pg_row))
194 }
195
196 async fn insert_many(&self, table: &str, rows: Vec<Row>) -> Result<u64> {
197 let mut tx = self.pool.begin().await.map_err(|e| RusticxError::Transaction(e.to_string()))?;
198 let mut count = 0u64;
199 for row in rows {
200 let pairs: Vec<(String, Value)> = row.into_iter().collect();
201 let (sql, bindings) = self.compiler().insert(table, &pairs);
202 let mut q = sqlx::query(&sql);
203 for val in bindings {
204 q = bind_value(q, val);
205 }
206 q.execute(&mut *tx).await.map_err(|e| RusticxError::Query(e.to_string()))?;
207 count += 1;
208 }
209 tx.commit().await.map_err(|e| RusticxError::Transaction(e.to_string()))?;
210 Ok(count)
211 }
212
213 async fn find(&self, query: &QueryBuilder) -> Result<Vec<Row>> {
214 let (sql, bindings) = self.compiler().select(query);
215 debug!(sql = %sql, "find");
216 self.fetch_rows_with_bindings(&sql, bindings).await
217 }
218
219 async fn find_one(&self, query: &QueryBuilder) -> Result<Option<Row>> {
220 let mut qb = query.clone();
221 qb.limit = Some(1);
222 let (sql, bindings) = self.compiler().select(&qb);
223 debug!(sql = %sql, "find_one");
224 let rows = self.fetch_rows_with_bindings(&sql, bindings).await?;
225 Ok(rows.into_iter().next())
226 }
227
228 async fn update(&self, query: &QueryBuilder) -> Result<u64> {
229 let (sql, bindings) = self.compiler().update(query);
230 debug!(sql = %sql, "update");
231 self.execute_with_bindings(&sql, bindings).await
232 }
233
234 async fn delete(&self, query: &QueryBuilder) -> Result<u64> {
235 let (sql, bindings) = self.compiler().delete(query);
236 debug!(sql = %sql, "delete");
237 self.execute_with_bindings(&sql, bindings).await
238 }
239
240 async fn count(&self, query: &QueryBuilder) -> Result<u64> {
241 let (sql, bindings) = self.compiler().count(query);
242 debug!(sql = %sql, "count");
243 let mut q = sqlx::query(&sql);
244 for val in bindings {
245 q = bind_value(q, val);
246 }
247 let row = q.fetch_one(&self.pool).await.map_err(|e| RusticxError::Query(e.to_string()))?;
248 let cnt: i64 = row.try_get("count").unwrap_or(0);
249 Ok(cnt as u64)
250 }
251
252 async fn execute_raw(&self, sql: &str, bindings: Vec<Value>) -> Result<u64> {
253 self.execute_with_bindings(sql, bindings).await
254 }
255
256 async fn query_raw(&self, sql: &str, bindings: Vec<Value>) -> Result<Vec<Row>> {
257 self.fetch_rows_with_bindings(sql, bindings).await
258 }
259}