Switchy Database
Database abstraction layer with support for multiple database backends, schema management, and transactions.
Overview
The Switchy Database package provides:
- Multi-Database Support: SQLite (rusqlite and sqlx), PostgreSQL (raw and sqlx), MySQL (sqlx), DuckDB, and Turso
- Schema Management: Create/alter tables, indexes with portable definitions
- Schema Introspection: Query existing database structure programmatically
- Transaction Support: ACID transactions with savepoint capabilities for nested transaction-like behavior
- Query Builder: Type-safe query construction for common operations
Features
Database Backends
- SQLite (rusqlite): File-based database using rusqlite driver with
?placeholders - SQLite (sqlx): File-based database using sqlx driver with
?placeholders - PostgreSQL (raw): Production PostgreSQL using tokio-postgres and deadpool-postgres
- PostgreSQL (sqlx): Production PostgreSQL using sqlx with connection pooling
- MySQL (sqlx): MySQL database using sqlx driver
- DuckDB: Embedded analytical database using duckdb driver with
?placeholders - Turso: Turso (local libSQL/SQLite-compatible) database support
- Simulator: Testing database (delegates to underlying backend)
Schema Features
- Schema Creation: Create tables, indexes, and alter existing schema
- Schema Introspection: Check table/column existence, get table metadata
- Type Portability: Common data type abstraction across backends
- Foreign Keys: Define and introspect foreign key relationships
- Auto-increment: Backend-specific auto-increment handling
Transaction Features
- ACID Transactions: Full transaction support across all backends
- Savepoints: Nested transaction-like behavior with rollback points
- Connection Pooling: Efficient connection management (backend-dependent)
Usage
Basic Query Operations
use ;
async
Transactions
use ;
async
async
Savepoints (Nested Transactions)
Savepoints allow partial rollback within a transaction, enabling complex error recovery:
use ;
async
async
Backend Support
| Database | Savepoint Support | Notes |
|---|---|---|
| SQLite | ✅ Full | Can create savepoints after errors |
| PostgreSQL | ✅ Full | Must create before potential errors |
| MySQL | ✅ Full (InnoDB) | Requires InnoDB storage engine |
| DuckDB | ❌ Not supported | Returns UnsupportedOperation |
Common Use Cases
- Batch Processing: Process large datasets with per-batch recovery
- Migration Testing: Test schema changes with rollback capability
- Complex Business Logic: Multi-step operations with conditional rollback
- Error Recovery: Continue transaction after handling specific errors
Schema Management
use ;
use ;
use DatabaseValue;
async
Schema Introspection
use ;
async
Raw SQL Queries
use ;
async
Feature Flags
The following feature flags are available in Cargo.toml:
Backend Features
sqlite-rusqlite- SQLite backend using rusqlite driversqlite-sqlx- SQLite backend using sqlx driverpostgres-raw- PostgreSQL backend using tokio-postgrespostgres-sqlx- PostgreSQL backend using sqlxmysql/mysql-sqlx- MySQL backend using sqlxduckdb- DuckDB embedded analytical databaseduckdb-bundled- DuckDB with bundled library (no system install required)turso- Turso local database support (file-based or in-memory)
Additional Features
schema- Schema management and introspection (enabled by default)cascade- CASCADE deletion support for schema operationsauto-reverse- Auto-reverse migration supportsimulator- Database simulator for testingdecimal- Decimal type support (rust_decimal)uuid- UUID type supportapi- Actix-web integration for web APIs
Placeholder Features
all-placeholders- Support for all placeholder stylesplaceholder-question-mark-?placeholder supportplaceholder-dollar-number-$1, $2placeholder supportplaceholder-at-number-@1, @2placeholder supportplaceholder-colon-number-:1, :2placeholder supportplaceholder-named-colon-:nameplaceholder support
Error Handling
use ;
// Within an async function with access to db: &dyn Database and track_id
match db.select.where_eq.execute_first.await
Backend-Specific Errors
Each backend has its own error variant:
DatabaseError::Rusqlite(rusqlite::RusqliteDatabaseError)- rusqlite backend errorsDatabaseError::SqliteSqlx(sqlx::sqlite::SqlxDatabaseError)- sqlx SQLite errorsDatabaseError::Postgres(postgres::postgres::PostgresDatabaseError)- raw PostgreSQL errorsDatabaseError::PostgresSqlx(sqlx::postgres::SqlxDatabaseError)- sqlx PostgreSQL errorsDatabaseError::MysqlSqlx(sqlx::mysql::SqlxDatabaseError)- sqlx MySQL errorsDatabaseError::DuckDb(duckdb::DuckDbDatabaseError)- DuckDB errorsDatabaseError::Turso(turso::TursoDatabaseError)- Turso errors
Data Types
The DatabaseValue enum supports the following types:
- Strings:
String,StringOpt - Booleans:
Bool,BoolOpt - Integers:
Int8,Int16,Int32,Int64(and unsigned variants) - Floating Point:
Real32,Real64 - Decimal:
Decimal(withdecimalfeature) - UUID:
Uuid(withuuidfeature) - DateTime:
DateTime,Now,NowPlus - Null:
Null
The schema::DataType enum provides database-agnostic type definitions:
Text- Variable-length textVarChar(n)- Fixed-length stringBool- BooleanInt- 32-bit integerSmallInt- 16-bit integerBigInt- 64-bit integerReal- 32-bit floating pointDouble- 64-bit floating pointDateTime- Date and timeDecimal(precision, scale)- Fixed-precision decimal
Architecture
Database Trait
The core Database trait provides:
- Query builder methods (
select,insert,update,delete,upsert) - Schema methods (
create_table,drop_table,create_index,alter_table) - requiresschemafeature - Execution methods (
query,query_first,exec_update,exec_insert, etc.) - Raw SQL methods (
query_raw,query_raw_params,exec_raw,exec_raw_params) - Introspection methods (
table_exists,column_exists,get_table_info,list_tables) - requiresschemafeature - Transaction method (
begin_transaction)
DatabaseTransaction Trait
The DatabaseTransaction trait extends Database with:
commit()- Commit the transactionrollback()- Rollback the transactionsavepoint(name)- Create a savepoint within the transaction- CASCADE operations (with
cascadefeature)
Savepoint Trait
The Savepoint trait provides:
release()- Commit the savepointrollback_to()- Rollback to the savepointname()- Get the savepoint name
Global and Profile Database Access
The crate also exposes public APIs for resolving Database instances in applications:
config::init(database)- Register a globalArc<Box<dyn Database>>singletonconfig::ConfigDatabase- Wrapper that dereferences todyn Database(and supports Actix extraction withapifeature)profiles::PROFILES- Global profile registry for multiple named databasesprofiles::LibraryDatabase- Profile-resolved wrapper that dereferences todyn Database(and supports Actix extraction withapifeature)
Backend Implementation Details
SQLite
Two SQLite implementations are available:
-
rusqlite (
sqlite-rusqlitefeature):- Uses
?placeholders - Blocking operations wrapped in async
- Connection pooling for concurrent transactions
- Uses
-
sqlx (
sqlite-sqlxfeature):- Uses
?placeholders - Native async support
- Built-in connection pooling
- Uses
PostgreSQL
Two PostgreSQL implementations are available:
-
Raw (
postgres-rawfeature):- Uses tokio-postgres and deadpool-postgres
- Uses
$1, $2placeholders - Custom connection pool management
-
sqlx (
postgres-sqlxfeature):- Uses sqlx driver
- Uses
$1, $2placeholders - Built-in connection pooling
MySQL
One MySQL implementation using sqlx:
- sqlx (
mysql-sqlxfeature):- Uses
?placeholders (via transformation) - Built-in connection pooling
- Full transaction support
- Uses
Turso
Turso (libSQL/SQLite-compatible) local database support:
- Uses
?placeholders - Local file-based and in-memory database support
- Compatible with SQLite API
DuckDB
Embedded analytical database:
- DuckDB (
duckdb/duckdb-bundledfeature):- Uses
?placeholders - Connection pool of 5 connections behind
Arc<Mutex<>> - Supports routing/consistency config (
DuckDbConfig):Deterministicmode (default): one shared connection topology for predictable behaviorPooledmode: round-robin across independent connectionsStrictconsistency (default): serialized operation gate in pooled modeRelaxedconsistency: higher throughput, weaker cross-connection guarantees
- Blocking operations wrapped in async
- In-memory and file-backed databases supported
- For cross-connection transaction behavior, prefer file-backed databases in tests
NOW()is cast toTIMESTAMP(DuckDB'sNOW()returnsTIMESTAMP WITH TIME ZONE)- Auto-increment uses
CREATE SEQUENCE+DEFAULT nextval(...)instead ofGENERATED ALWAYS AS IDENTITY - Savepoints are not supported (returns
UnsupportedOperation) - Schema introspection via
information_schemaandduckdb_indexes() DELETE ... RETURNINGuses a SELECT-then-DELETE workaround (see source for details)DROP TABLE ... CASCADEfor FK-dependent tables is limited in DuckDB v1.4.4ALTER TABLE ... DROP COLUMNwith index/FK dependencies is limited in DuckDB v1.4.4
- Uses
Limitations
- No ORM: This is a query builder, not a full ORM with automatic relationship mapping
- No Migration System: No built-in migration versioning or rollback system
- Manual Schema Management: Schema changes must be managed manually
- No Query Optimization: No automatic query analysis or optimization
- Backend-Specific Placeholder Syntax: Different backends require different placeholder styles (though some auto-transformation is provided)
See Also
- MoosicBox Config - Configuration management
- MoosicBox Server - Server with database integration