Crate sql_middleware

Crate sql_middleware 

Source
Expand description

§SQL Middleware - A lightweight wrapper SQL backends

This crate provides a lightweight async wrapper for SQLite, PostgreSQL, LibSQL, Turso (experimental), and SQL Server (tiberius). The goal is a similar, async-compatible API consistent across databases.

§Features

  • Similar API regardless of backend (as much as possible)
  • Asynchronous (where available)
  • deadpool connection pooling (where available)
  • Transaction support
  • Not an ORM

§Feature Flags

Default features are sqlite and postgres. Enable others as needed:

# Only SQLite and LibSQL
sql-middleware = { version = "0", features = ["sqlite", "libsql"] }

# All backends
sql-middleware = { version = "0", features = ["sqlite", "postgres", "mssql", "libsql", "turso"] }

Additional flags:

  • libsql: LibSQL (local has been tested, remote is present)
  • turso: Turso (in-process, SQLite-compatible). Experimental; no remote support.
  • test-utils: Test helpers for internal testing
  • mssql: SQL Server via tiberius (untested, but present)
  • benchmarks: Criterion helpers for benches

§Example

use sql_middleware::prelude::*;

async fn sqlite_example() -> Result<(), SqlMiddlewareDbError> {
    // Create a SQLite connection pool
    let config = ConfigAndPool::new_sqlite("my_database.db".to_string()).await?;
    
    // Get a connection from the pool
    let pool = config.pool.get().await?;
    let mut conn = MiddlewarePool::get_connection(&pool).await?;
    
    // Execute a query with parameters
    let result = conn.execute_select(
        "SELECT * FROM users WHERE id = ?",
        &[RowValues::Int(1)]
    ).await?;
    
    // Process the results
    for row in result.results {
        println!("User: {}", row.get("name").unwrap().as_text().unwrap());
    }
    
    Ok(())
}

async fn postgres_example() -> Result<(), SqlMiddlewareDbError> {
    // Create a PostgreSQL connection pool
    let mut pg_config = deadpool_postgres::Config::new();
    pg_config.host = Some("localhost".to_string());
    pg_config.port = Some(5432);
    pg_config.dbname = Some("mydatabase".to_string());
    pg_config.user = Some("user".to_string());
    pg_config.password = Some("password".to_string());
    
    let config = ConfigAndPool::new_postgres(pg_config).await?;
    
    // Get a connection and execute a query
    let pool = config.pool.get().await?;
    let mut conn = MiddlewarePool::get_connection(&pool).await?;
    
    let result = conn.execute_select(
        "SELECT * FROM users WHERE id = $1",
        &[RowValues::Int(1)]
    ).await?;
    
    Ok(())
}

async fn libsql_example() -> Result<(), SqlMiddlewareDbError> {
    use sql_middleware::prelude::*;

    // In-memory LibSQL (or use a file path like "./data.db")
    let config = ConfigAndPool::new_libsql(":memory:".to_string()).await?;

    // Remote LibSQL (Turso) example:
    // let config = ConfigAndPool::new_libsql_remote(
    //     "libsql://your-url".to_string(),
    //     "your_auth_token".to_string(),
    // ).await?;

    let pool = config.pool.get().await?;
    let mut conn = MiddlewarePool::get_connection(&pool).await?;

    let result = conn.execute_select(
        "SELECT 1 as one",
        &[]
    ).await?;

    assert_eq!(*result.results[0].get("one").unwrap().as_int().unwrap(), 1);
    Ok(())
}

async fn turso_example() -> Result<(), SqlMiddlewareDbError> {
    use sql_middleware::prelude::*;

    // In-memory Turso (or use a file path like "./data.db")
    let config = ConfigAndPool::new_turso(":memory:".to_string()).await?;

    let pool = config.pool.get().await?;
    let mut conn = MiddlewarePool::get_connection(&pool).await?;

    conn.execute_batch("CREATE TABLE t (id INTEGER, name TEXT);").await?;
    conn.execute_dml(
        "INSERT INTO t (id, name) VALUES (?, ?)",
        &[RowValues::Int(1), RowValues::Text("alice".into())],
    ).await?;

    let rs = conn
        .execute_select("SELECT name FROM t WHERE id = ?", &[RowValues::Int(1)])
        .await?;
    assert_eq!(rs.results[0].get("name").unwrap().as_text().unwrap(), "alice");
    Ok(())
}

async fn sqlserver_example() -> Result<(), SqlMiddlewareDbError> {
    // Create an SQL Server connection pool
    let config = ConfigAndPool::new_mssql(
        "localhost".to_string(),
        "mydatabase".to_string(),
        "sa".to_string(),
        "strong_password".to_string(),
        Some(1433),
        None,
    ).await?;
    
    // Get a connection and execute a query
    let pool = config.pool.get().await?;
    let mut conn = MiddlewarePool::get_connection(&pool).await?;
    
    let result = conn.execute_select(
        "SELECT * FROM users WHERE id = @p1",
        &[RowValues::Int(1)]
    ).await?;
    
    Ok(())
}

// For more in-depth examples (parameter conversion, batch queries, per-backend
// transactions, AsyncDatabaseExecutor usage), see the project README:
// https://github.com/derekfrye/sql-middleware/blob/main/docs/README.md

Re-exports§

pub use middleware::AnyConnWrapper;
pub use middleware::AsyncDatabaseExecutor;
pub use middleware::ConfigAndPool;
pub use middleware::ConversionMode;
pub use middleware::CustomDbRow;
pub use middleware::DatabaseType;
pub use middleware::MiddlewarePool;
pub use middleware::MiddlewarePoolConnection;
pub use middleware::ParamConverter;
pub use middleware::QueryAndParams;
pub use middleware::ResultSet;
pub use middleware::RowValues;
pub use middleware::SqlMiddlewareDbError;
pub use conversion::convert_sql_params;
pub use exports::*;

Modules§

conversion
Parameter conversion utilities.
error
executor
exports
Database-specific type exports.
helpers
Helper utilities for testing and development.
middleware
pool
postgres
prelude
Convenient imports for common functionality.
query
results
test_helpers
types