1use std::sync::Arc;
5
6use keelson_core::Value;
7use keelson_exec::{
8 Begin, BeginWith, Column, ExecError, ExecFuture, ExecResult, Executor, Family, RawConnection,
9 Row, RowStream, Statement, StreamExecutor, Transaction, TxConflict, TxConflictError, TxOptions,
10};
11use sqlx::postgres::{PgArgumentBuffer, PgArguments, PgRow, PgTypeInfo};
12use sqlx::{Column as _, Postgres, Row as _, TypeInfo as _, ValueRef as _};
13
14use crate::common::{decode_err, unhandled};
15
16#[derive(Debug, Clone)]
19pub struct Pool {
20 inner: sqlx::PgPool,
21}
22
23impl Pool {
24 pub async fn connect(url: &str) -> Result<Self, ExecError> {
26 let inner = sqlx::postgres::PgPoolOptions::new()
27 .connect(url)
28 .await
29 .map_err(ExecError::driver)?;
30 Ok(Pool { inner })
31 }
32
33 pub fn from_pool(inner: sqlx::PgPool) -> Self {
35 Pool { inner }
36 }
37
38 pub fn inner(&self) -> &sqlx::PgPool {
40 &self.inner
41 }
42}
43
44impl Executor for Pool {
45 fn family(&self) -> Family {
46 Family::Postgres
47 }
48
49 fn fetch(&self, stmt: Statement) -> ExecFuture<'_, Result<Vec<Row>, ExecError>> {
50 Box::pin(async move {
51 let Statement { sql, args, .. } = stmt;
52 do_fetch(&self.inner, &sql, args).await
53 })
54 }
55
56 fn execute(&self, stmt: Statement) -> ExecFuture<'_, Result<ExecResult, ExecError>> {
57 Box::pin(async move {
58 let Statement { sql, args, .. } = stmt;
59 do_execute(&self.inner, &sql, args).await
60 })
61 }
62}
63
64impl Begin for Pool {
65 fn begin(&self) -> ExecFuture<'_, Result<Transaction, ExecError>> {
66 Box::pin(async move {
67 let conn = self.inner.acquire().await.map_err(ExecError::driver)?;
68 Transaction::begin_on(Box::new(RawConn { conn })).await
69 })
70 }
71}
72
73impl BeginWith for Pool {
74 fn begin_with(&self, opts: TxOptions) -> ExecFuture<'_, Result<Transaction, ExecError>> {
75 Box::pin(async move {
76 opts.check(Family::Postgres)?;
79 let conn = self.inner.acquire().await.map_err(ExecError::driver)?;
80 Transaction::begin_on_with(Box::new(RawConn { conn }), opts).await
81 })
82 }
83}
84
85impl StreamExecutor for Pool {
86 fn fetch_stream(&self, stmt: Statement) -> ExecFuture<'_, Result<RowStream, ExecError>> {
87 Box::pin(async move {
88 let pool = self.inner.clone();
89 let (tx, rx) = tokio::sync::mpsc::channel::<Result<Row, ExecError>>(64);
90 tokio::spawn(async move {
91 use futures_util::StreamExt as _;
92 let Statement { sql, args, .. } = stmt;
93 let q = match bind_args(&sql, args) {
94 Ok(q) => q,
95 Err(e) => {
96 let _ = tx.send(Err(e)).await;
97 return;
98 }
99 };
100 let mut native = q.fetch(&pool);
101 let mut header: Option<Arc<[Column]>> = None;
102 while let Some(next) = native.next().await {
103 let msg = match next {
104 Ok(row) => decode_row(&row, &mut header),
105 Err(e) => Err(ExecError::driver(e)),
106 };
107 let stop = msg.is_err();
108 if tx.send(msg).await.is_err() || stop {
109 return;
110 }
111 }
112 });
113 Ok(RowStream::new(rx))
114 })
115 }
116}
117
118#[derive(Debug)]
120struct RawConn {
121 conn: sqlx::pool::PoolConnection<Postgres>,
122}
123
124impl RawConnection for RawConn {
125 fn family(&self) -> Family {
126 Family::Postgres
127 }
128
129 fn fetch<'a>(
130 &'a mut self,
131 sql: &'a str,
132 args: Vec<Value>,
133 ) -> ExecFuture<'a, Result<Vec<Row>, ExecError>> {
134 Box::pin(async move { do_fetch(&mut *self.conn, sql, args).await })
135 }
136
137 fn execute<'a>(
138 &'a mut self,
139 sql: &'a str,
140 args: Vec<Value>,
141 ) -> ExecFuture<'a, Result<ExecResult, ExecError>> {
142 Box::pin(async move { do_execute(&mut *self.conn, sql, args).await })
143 }
144
145 fn abandon(self: Box<Self>) {
146 let _ = self.conn.detach();
149 }
150}
151
152fn driver_err(e: sqlx::Error) -> ExecError {
159 if let sqlx::Error::Database(db) = &e {
160 let kind = match db.code().as_deref() {
161 Some("40001") => Some(TxConflict::Serialization),
162 Some("40P01") => Some(TxConflict::Deadlock),
163 Some("55P03") => Some(TxConflict::LockTimeout),
164 _ => None,
165 };
166 if let Some(kind) = kind {
167 let code = db.code().unwrap_or_default().into_owned();
168 let message = db.message().to_owned();
169 return TxConflictError::new(kind, code, message)
170 .with_source(e)
171 .into_exec_error();
172 }
173 }
174 ExecError::driver(e)
175}
176
177async fn do_fetch<'e, E>(exec: E, sql: &str, args: Vec<Value>) -> Result<Vec<Row>, ExecError>
178where
179 E: sqlx::Executor<'e, Database = Postgres>,
180{
181 let rows = bind_args(sql, args)?
182 .fetch_all(exec)
183 .await
184 .map_err(driver_err)?;
185 let mut header: Option<Arc<[Column]>> = None;
186 rows.iter().map(|r| decode_row(r, &mut header)).collect()
187}
188
189async fn do_execute<'e, E>(exec: E, sql: &str, args: Vec<Value>) -> Result<ExecResult, ExecError>
190where
191 E: sqlx::Executor<'e, Database = Postgres>,
192{
193 let done = if args.is_empty() {
198 exec.execute(sql).await.map_err(driver_err)?
199 } else {
200 bind_args(sql, args)?
201 .execute(exec)
202 .await
203 .map_err(driver_err)?
204 };
205 Ok(ExecResult::new(done.rows_affected(), None))
207}
208
209#[derive(Debug)]
213struct UnknownNull;
214
215impl sqlx::Type<Postgres> for UnknownNull {
216 fn type_info() -> PgTypeInfo {
217 PgTypeInfo::with_name("unknown")
218 }
219}
220
221impl sqlx::Encode<'_, Postgres> for UnknownNull {
222 fn encode_by_ref(
223 &self,
224 _buf: &mut PgArgumentBuffer,
225 ) -> Result<sqlx::encode::IsNull, sqlx::error::BoxDynError> {
226 Ok(sqlx::encode::IsNull::Yes)
227 }
228}
229
230fn bind_args<'q>(
234 sql: &'q str,
235 args: Vec<Value>,
236) -> Result<sqlx::query::Query<'q, Postgres, PgArguments>, ExecError> {
237 let mut q = sqlx::query(sql);
238 for v in args {
239 q = match v {
240 Value::Null => q.bind(UnknownNull),
241 Value::Bool(x) => q.bind(x),
242 Value::I8(x) => q.bind(i16::from(x)),
246 Value::I16(x) => q.bind(x),
247 Value::I32(x) => q.bind(x),
248 Value::I64(x) => q.bind(x),
249 Value::U8(x) => q.bind(i16::from(x)),
250 Value::U16(x) => q.bind(i32::from(x)),
251 Value::U32(x) => q.bind(i64::from(x)),
252 Value::U64(x) => {
253 q.bind(i64::try_from(x).map_err(|_| unsupported_value("u64 out of i64 range"))?)
254 }
255 Value::F32(x) => q.bind(x),
256 Value::F64(x) => q.bind(x),
257 Value::Text(x) => q.bind(x),
258 Value::Bytes(x) => q.bind(x),
259 Value::Array(items) => bind_array(q, items)?,
260 #[cfg(feature = "chrono")]
261 Value::Date(x) => q.bind(x),
262 #[cfg(feature = "chrono")]
263 Value::Time(x) => q.bind(x),
264 #[cfg(feature = "chrono")]
265 Value::DateTime(x) => q.bind(x),
266 #[cfg(feature = "chrono")]
267 Value::TimestampTz(x) => q.bind(x),
268 #[cfg(feature = "uuid")]
269 Value::Uuid(x) => q.bind(x),
270 #[cfg(feature = "decimal")]
271 Value::Decimal(x) => q.bind(x),
272 #[cfg(feature = "json")]
273 Value::Json(x) => q.bind(x),
274 other => return Err(unsupported_value(other.type_name())),
275 };
276 }
277 Ok(q)
278}
279
280fn unsupported_value(type_name: &'static str) -> ExecError {
281 ExecError::UnsupportedValue {
282 type_name,
283 family: Family::Postgres,
284 }
285}
286
287fn bind_array<'q>(
292 q: sqlx::query::Query<'q, Postgres, PgArguments>,
293 items: Vec<Value>,
294) -> Result<sqlx::query::Query<'q, Postgres, PgArguments>, ExecError> {
295 macro_rules! typed {
296 ($variant:ident, $t:ty) => {{
297 let mut out: Vec<Option<$t>> = Vec::with_capacity(items.len());
298 for item in items {
299 match item {
300 Value::$variant(x) => out.push(Some(x)),
301 Value::Null => out.push(None),
302 other => return Err(unsupported_value(other.type_name())),
303 }
304 }
305 q.bind(out)
306 }};
307 }
308 let first = items.iter().find(|v| !v.is_null());
309 Ok(match first {
310 None => {
311 let nulls: Vec<Option<String>> = items.iter().map(|_| None).collect();
313 q.bind(nulls)
314 }
315 Some(Value::Bool(_)) => typed!(Bool, bool),
316 Some(Value::I16(_)) => typed!(I16, i16),
317 Some(Value::I32(_)) => typed!(I32, i32),
318 Some(Value::I64(_)) => typed!(I64, i64),
319 Some(Value::F32(_)) => typed!(F32, f32),
320 Some(Value::F64(_)) => typed!(F64, f64),
321 Some(Value::Text(_)) => typed!(Text, String),
322 #[cfg(feature = "uuid")]
323 Some(Value::Uuid(_)) => typed!(Uuid, uuid::Uuid),
324 Some(other) => return Err(unsupported_value(other.type_name())),
325 })
326}
327
328fn decode_row(row: &PgRow, header: &mut Option<Arc<[Column]>>) -> Result<Row, ExecError> {
331 let columns = header
332 .get_or_insert_with(|| {
333 row.columns()
334 .iter()
335 .map(|c| Column::new(c.name()))
336 .collect::<Vec<_>>()
337 .into()
338 })
339 .clone();
340 let mut values = Vec::with_capacity(row.columns().len());
341 for i in 0..row.columns().len() {
342 values.push(decode_value(row, i)?);
343 }
344 Ok(Row::new(columns, values))
345}
346
347fn decode_value(row: &PgRow, i: usize) -> Result<Value, ExecError> {
348 let col = &row.columns()[i];
349 let name = col.name();
350 let raw = row.try_get_raw(i).map_err(|e| decode_err(name, e))?;
351 if raw.is_null() {
352 return Ok(Value::Null);
353 }
354 let ty = raw.type_info();
355 let ty = ty.name();
356
357 macro_rules! take {
358 ($t:ty) => {
359 row.try_get::<$t, _>(i).map_err(|e| decode_err(name, e))?
360 };
361 }
362
363 Ok(match ty {
364 "BOOL" => Value::Bool(take!(bool)),
365 "INT2" => Value::I16(take!(i16)),
366 "INT4" => Value::I32(take!(i32)),
367 "INT8" => Value::I64(take!(i64)),
368 "FLOAT4" => Value::F32(take!(f32)),
369 "FLOAT8" => Value::F64(take!(f64)),
370 "TEXT" | "VARCHAR" | "BPCHAR" | "NAME" => Value::Text(take!(String)),
371 "BYTEA" => Value::Bytes(take!(Vec<u8>)),
372 #[cfg(feature = "chrono")]
373 "DATE" => Value::Date(take!(chrono::NaiveDate)),
374 #[cfg(feature = "chrono")]
375 "TIME" => Value::Time(take!(chrono::NaiveTime)),
376 #[cfg(feature = "chrono")]
377 "TIMESTAMP" => Value::DateTime(take!(chrono::NaiveDateTime)),
378 #[cfg(feature = "chrono")]
379 "TIMESTAMPTZ" => Value::TimestampTz(take!(chrono::DateTime<chrono::Utc>)),
380 #[cfg(not(feature = "chrono"))]
381 "DATE" | "TIME" | "TIMESTAMP" | "TIMESTAMPTZ" => {
382 return Err(crate::common::need_feature(name, ty, "chrono"));
383 }
384 #[cfg(feature = "uuid")]
385 "UUID" => Value::Uuid(take!(uuid::Uuid)),
386 #[cfg(not(feature = "uuid"))]
387 "UUID" => return Err(crate::common::need_feature(name, ty, "uuid")),
388 #[cfg(feature = "decimal")]
389 "NUMERIC" => Value::Decimal(take!(rust_decimal::Decimal)),
390 #[cfg(not(feature = "decimal"))]
391 "NUMERIC" => return Err(crate::common::need_feature(name, ty, "decimal")),
392 #[cfg(feature = "json")]
393 "JSON" | "JSONB" => Value::Json(take!(serde_json::Value)),
394 #[cfg(not(feature = "json"))]
395 "JSON" | "JSONB" => return Err(crate::common::need_feature(name, ty, "json")),
396 "BOOL[]" => array(take!(Vec<Option<bool>>), Value::Bool),
397 "INT2[]" => array(take!(Vec<Option<i16>>), Value::I16),
398 "INT4[]" => array(take!(Vec<Option<i32>>), Value::I32),
399 "INT8[]" => array(take!(Vec<Option<i64>>), Value::I64),
400 "FLOAT4[]" => array(take!(Vec<Option<f32>>), Value::F32),
401 "FLOAT8[]" => array(take!(Vec<Option<f64>>), Value::F64),
402 "TEXT[]" | "VARCHAR[]" => array(take!(Vec<Option<String>>), Value::Text),
403 #[cfg(feature = "uuid")]
404 "UUID[]" => array(take!(Vec<Option<uuid::Uuid>>), Value::Uuid),
405 other => return Err(unhandled(name, other)),
406 })
407}
408
409fn array<T>(items: Vec<Option<T>>, wrap: fn(T) -> Value) -> Value {
410 Value::Array(
411 items
412 .into_iter()
413 .map(|x| x.map_or(Value::Null, wrap))
414 .collect(),
415 )
416}