Files
cdk/crates/cdk-postgres/src/value.rs
C 28a01398fd Add PostgreSQL support for mint and wallet (#878)
* Add PostgreSQL support for mint and wallet

* Fixed bug to avoid empty calls `get_proofs_states`

* Fixed SQL bug

* Avoid redudant clone()

* Add more tests for the storage layer

* Minor enhacements

* Add a generic function to execute db operations

This function would log slow operations and log errors

* Provision a postgres db for tests

* Update deps for msrv

* Add postgres to pipeline

* feat: add psgl to example and docker

* feat: db url fmt

---------

Co-authored-by: thesimplekid <tsk@thesimplekid.com>
2025-08-18 17:45:11 +01:00

131 lines
4.2 KiB
Rust

use std::fmt::Debug;
use cdk_sql_common::value::Value;
use tokio_postgres::types::{self, FromSql, ToSql};
#[derive(Debug)]
pub enum PgValue<'a> {
Null,
Integer(i64),
Real(f64),
Text(&'a str),
Blob(&'a [u8]),
}
impl<'a> From<&'a Value> for PgValue<'a> {
fn from(value: &'a Value) -> Self {
match value {
Value::Blob(b) => PgValue::Blob(b),
Value::Text(text) => PgValue::Text(text.as_str()),
Value::Null => PgValue::Null,
Value::Integer(i) => PgValue::Integer(*i),
Value::Real(r) => PgValue::Real(*r),
}
}
}
impl<'a> From<PgValue<'a>> for Value {
fn from(val: PgValue<'a>) -> Self {
match val {
PgValue::Blob(value) => Value::Blob(value.to_owned()),
PgValue::Text(value) => Value::Text(value.to_owned()),
PgValue::Null => Value::Null,
PgValue::Integer(n) => Value::Integer(n),
PgValue::Real(r) => Value::Real(r),
}
}
}
impl<'a> FromSql<'a> for PgValue<'a> {
fn accepts(_ty: &types::Type) -> bool {
true
}
fn from_sql(
ty: &types::Type,
raw: &'a [u8],
) -> Result<Self, Box<dyn std::error::Error + Sync + Send>> {
Ok(match *ty {
types::Type::VARCHAR | types::Type::TEXT | types::Type::BPCHAR | types::Type::NAME => {
PgValue::Text(<&str as FromSql>::from_sql(ty, raw)?)
}
types::Type::BOOL => PgValue::Integer(if <bool as FromSql>::from_sql(ty, raw)? {
1
} else {
0
}),
types::Type::INT2 => PgValue::Integer(<i8 as FromSql>::from_sql(ty, raw)? as i64),
types::Type::INT4 => PgValue::Integer(<i32 as FromSql>::from_sql(ty, raw)? as i64),
types::Type::INT8 => PgValue::Integer(<i64 as FromSql>::from_sql(ty, raw)?),
types::Type::BIT_ARRAY | types::Type::BYTEA | types::Type::UNKNOWN => {
PgValue::Blob(<&[u8] as FromSql>::from_sql(ty, raw)?)
}
_ => panic!("Unsupported type {ty:?}"),
})
}
fn from_sql_null(_ty: &types::Type) -> Result<Self, Box<dyn std::error::Error + Sync + Send>> {
Ok(PgValue::Null)
}
}
impl ToSql for PgValue<'_> {
fn to_sql(
&self,
ty: &types::Type,
out: &mut types::private::BytesMut,
) -> Result<types::IsNull, Box<dyn std::error::Error + Sync + Send>>
where
Self: Sized,
{
match self {
PgValue::Blob(blob) => (*blob).to_sql(ty, out),
PgValue::Text(text) => (*text).to_sql(ty, out),
PgValue::Null => Ok(types::IsNull::Yes),
PgValue::Real(r) => r.to_sql(ty, out),
PgValue::Integer(i) => match *ty {
types::Type::BOOL => (*i != 0).to_sql(ty, out),
types::Type::INT2 => (*i as i16).to_sql(ty, out),
types::Type::INT4 => (*i as i32).to_sql(ty, out),
_ => i.to_sql_checked(ty, out),
},
}
}
fn accepts(_ty: &types::Type) -> bool
where
Self: Sized,
{
true
}
fn encode_format(&self, ty: &types::Type) -> types::Format {
match self {
PgValue::Blob(blob) => blob.encode_format(ty),
PgValue::Text(text) => text.encode_format(ty),
PgValue::Null => types::Format::Text,
PgValue::Real(r) => r.encode_format(ty),
PgValue::Integer(i) => i.encode_format(ty),
}
}
fn to_sql_checked(
&self,
ty: &types::Type,
out: &mut types::private::BytesMut,
) -> Result<types::IsNull, Box<dyn std::error::Error + Sync + Send>> {
match self {
PgValue::Blob(blob) => blob.to_sql_checked(ty, out),
PgValue::Text(text) => text.to_sql_checked(ty, out),
PgValue::Null => Ok(types::IsNull::Yes),
PgValue::Real(r) => r.to_sql_checked(ty, out),
PgValue::Integer(i) => match *ty {
types::Type::BOOL => (*i != 0).to_sql_checked(ty, out),
types::Type::INT2 => (*i as i16).to_sql_checked(ty, out),
types::Type::INT4 => (*i as i32).to_sql_checked(ty, out),
_ => i.to_sql_checked(ty, out),
},
}
}
}