/
germanubis
/
sqlx
Обзор
Документация
Войти
/
germanubis
/
sqlx
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
examples/postgres/multi-database/payments/src/lib.rs
127 строк
4 KB
Austin Bonander
feat: create `sqlx.toml` format (#3383)
01 июл 2025, 02:34
Не верифицирован
01 июл 2025, 02:34
25cbeed
Код
Авторство
О чём код?
use accounts::{AccountId, AccountsManager}; use sqlx::postgres::{PgConnectOptions, PgPoolOptions}; use sqlx::{Acquire, PgConnection, PgPool, Postgres}; use time::OffsetDateTime; use uuid::Uuid; #[derive(sqlx::Type, Copy, Clone, Debug)] #[sqlx(transparent)] pub struct PaymentId(pub Uuid); #[derive(sqlx::Type, Copy, Clone, Debug)] #[sqlx(type_name = "payment_status")] #[sqlx(rename_all = "snake_case")] pub enum PaymentStatus { Pending, Created, Success, Failed, } // Users often assume that they need `#[derive(FromRow)]` to use `query_as!()`, // then are surprised when the derive's control attributes have no effect. // The macros currently do *not* use the `FromRow` trait at all. // Support for `FromRow` is planned, but would require significant changes to the macros. // See https://github.com/launchbadge/sqlx/issues/514 for details. #[derive(Clone, Debug)] pub struct Payment { pub payment_id: PaymentId, pub account_id: AccountId, pub status: PaymentStatus, pub currency: String, // `rust_decimal::Decimal` has more than enough precision for any real-world amount of money. pub amount: rust_decimal::Decimal, pub external_payment_id: Option<String>, pub created_at: OffsetDateTime, pub updated_at: Option<OffsetDateTime>, } pub struct PaymentsManager { pool: PgPool, } impl PaymentsManager { pub async fn setup(opts: PgConnectOptions) -> sqlx::Result<Self> { let pool = PgPoolOptions::new() .max_connections(5) .connect_with(opts) .await?; sqlx::migrate!().run(&pool).await?; Ok(Self { pool }) } /// # Note /// For simplicity, this does not ensure that `account_id` actually exists. pub async fn create( &self, account_id: AccountId, currency: &str, amount: rust_decimal::Decimal, ) -> sqlx::Result<Payment> { // Check-out a connection to avoid paying the overhead of acquiring one for each call. let mut conn = self.pool.acquire().await?; // Imagine this method does more than just create a record in the database; // maybe it actually initiates the payment with a third-party vendor, like Stripe. // // We need to ensure that we can link the payment in the vendor's systems back to a record // in ours, even if any of the following happens: // * The application dies before storing the external payment ID in the database // * We lose the connection to the database while trying to commit a transaction // * The database server dies while committing the transaction // // Thus, we create the payment in three atomic phases: // * We create the payment record in our system and commit it. // * We create the payment in the vendor's system with our payment ID attached. // * We update our payment record with the vendor's payment ID. let payment_id = sqlx::query_scalar!( "insert into payment(account_id, status, currency, amount) \ values ($1, $2, $3, $4) \ returning payment_id", // The database doesn't give us enough information to correctly typecheck `AccountId` here. // We have to insert the UUID directly. account_id.0, PaymentStatus::Pending, currency, amount, ) .fetch_one(&mut *conn) .await?; // We then create the record with the payment vendor... let external_payment_id = "foobar1234"; // Then we store the external payment ID and update the payment status. // // NOTE: use caution with `select *` or `returning *`; // the order of columns gets baked into the binary, so if it changes between compile time and // run-time, you may run into errors. let payment = sqlx::query_as!( Payment, "update payment \ set status = $1, external_payment_id = $2 \ where payment_id = $3 \ returning *", PaymentStatus::Created, external_payment_id, payment_id.0, ) .fetch_one(&mut *conn) .await?; Ok(payment) } pub async fn get(&self, payment_id: PaymentId) -> sqlx::Result<Option<Payment>> { sqlx::query_as!( Payment, // see note above about `select *` "select * from payment where payment_id = $1", payment_id.0 ) .fetch_optional(&self.pool) .await } }