sqlx-record/.claude/skills/sqlx-conn-provider.md

5.1 KiB

sqlx-record ConnProvider Skill

Guide to flexible connection management.

Triggers

  • "connection provider", "conn provider"
  • "borrow connection", "pool connection"
  • "lazy connection", "connection management"

Overview

ConnProvider enables flexible connection handling:

  • Borrowed: Use an existing connection reference
  • Owned: Lazily acquire from pool on first use

Enum Variants

pub enum ConnProvider<'a> {
    /// Reference to existing connection
    Borrowed {
        conn: &'a mut PoolConnection<DB>,
    },
    /// Lazy acquisition from pool
    Owned {
        pool: Pool,
        conn: Option<PoolConnection<DB>>,
    },
}

Constructors

from_ref

Use an existing connection:

let mut conn = pool.acquire().await?;
let mut provider = ConnProvider::from_ref(&mut conn);

from_pool

Lazy acquisition from pool:

let mut provider = ConnProvider::from_pool(pool.clone());
// Connection acquired on first get_conn() call

Getting the Connection

let conn = provider.get_conn().await?;
// Returns &mut PoolConnection<DB>
  • Borrowed: Returns reference immediately
  • Owned: Acquires on first call, returns same connection on subsequent calls

Use Cases

Reuse Existing Connection

async fn process_batch(conn: &mut PoolConnection<MySql>) -> Result<()> {
    let mut provider = ConnProvider::from_ref(conn);

    do_work_a(&mut provider).await?;
    do_work_b(&mut provider).await?;  // Same connection
    do_work_c(&mut provider).await?;  // Same connection

    Ok(())
}

Lazy Pool Connection

async fn maybe_needs_db(pool: MySqlPool, condition: bool) -> Result<()> {
    let mut provider = ConnProvider::from_pool(pool);

    if condition {
        // Connection acquired here (first use)
        let conn = provider.get_conn().await?;
        sqlx::query("SELECT 1").execute(&mut **conn).await?;
    }
    // If condition is false, no connection was ever acquired

    Ok(())
}

Uniform Interface

async fn do_database_work(provider: &mut ConnProvider<'_>) -> Result<()> {
    let conn = provider.get_conn().await?;

    // Works regardless of Borrowed or Owned
    sqlx::query("INSERT INTO logs (msg) VALUES (?)")
        .bind("operation completed")
        .execute(&mut **conn)
        .await?;

    Ok(())
}

// Call with borrowed
let mut conn = pool.acquire().await?;
do_database_work(&mut ConnProvider::from_ref(&mut conn)).await?;

// Call with pool
do_database_work(&mut ConnProvider::from_pool(pool)).await?;

Transaction-like Patterns

async fn multi_step_operation(pool: MySqlPool) -> Result<()> {
    let mut provider = ConnProvider::from_pool(pool);

    // All operations use same connection
    step_1(&mut provider).await?;
    step_2(&mut provider).await?;
    step_3(&mut provider).await?;

    // Connection returned to pool when provider drops
    Ok(())
}

Database-Specific Types

The concrete types depend on the enabled feature:

Feature Pool Type Connection Type
mysql MySqlPool PoolConnection<MySql>
postgres PgPool PoolConnection<Postgres>
sqlite SqlitePool PoolConnection<Sqlite>

Example: Service Layer

use sqlx_record::prelude::*;

struct UserService;

impl UserService {
    async fn create_with_profile(
        provider: &mut ConnProvider<'_>,
        name: &str,
        bio: &str,
    ) -> Result<Uuid, Error> {
        let conn = provider.get_conn().await?;

        // Create user
        let user_id = new_uuid();
        sqlx::query("INSERT INTO users (id, name) VALUES (?, ?)")
            .bind(user_id)
            .bind(name)
            .execute(&mut **conn)
            .await?;

        // Create profile (same connection)
        sqlx::query("INSERT INTO profiles (user_id, bio) VALUES (?, ?)")
            .bind(user_id)
            .bind(bio)
            .execute(&mut **conn)
            .await?;

        Ok(user_id)
    }
}

// Usage
let mut provider = ConnProvider::from_pool(pool);
let user_id = UserService::create_with_profile(&mut provider, "Alice", "Hello!").await?;

Connection Lifecycle

from_pool(pool)          from_ref(&mut conn)
      │                         │
      ▼                         ▼
  Owned {                  Borrowed {
    pool,                    conn: &mut PoolConnection
    conn: None              }
  }                              │
      │                         │
      │ get_conn()              │ get_conn()
      ▼                         ▼
  pool.acquire()           return conn
      │                         │
      ▼                         │
  Owned {                       │
    pool,                       │
    conn: Some(acquired)        │
  }                              │
      │                         │
      │ get_conn() (subsequent) │
      ▼                         │
  return &mut acquired          │
      │                         │
      ▼                         ▼
  Drop: conn returned      Drop: nothing (borrowed)